├── .gitignore ├── .gitmodules ├── LICENSE ├── README.md ├── config.toml ├── content ├── _index.md └── page │ ├── deployments.md │ ├── diy.md │ ├── envs.md │ ├── healthz.md │ ├── ic.md │ ├── jobs.md │ ├── labels.md │ ├── logging.md │ ├── nodes.md │ ├── ns.md │ ├── pf.md │ ├── pods.md │ ├── proxy.md │ ├── pv.md │ ├── rcs.md │ ├── sd.md │ ├── secrets.md │ ├── services.md │ ├── statefulset.md │ └── volumes.md ├── specs ├── deployments │ ├── d09.yaml │ └── d10.yaml ├── envs │ └── pod.yaml ├── healthz │ ├── badpod.yaml │ ├── nap-rc.yaml │ ├── nap-svc.yaml │ ├── pod.yaml │ └── ready.yaml ├── ic │ └── deploy.yaml ├── jobs │ └── job.yaml ├── labels │ ├── anotherpod.yaml │ └── pod.yaml ├── logging │ ├── oneshotpod.yaml │ └── pod.yaml ├── nodes │ └── pod.yaml ├── ns │ ├── ns.yaml │ └── pod.yaml ├── pf │ └── app.yaml ├── pods │ ├── constraint-pod.yaml │ ├── oc-constraint-pod.yaml │ └── pod.yaml ├── pv │ ├── deploy.yaml │ ├── pv.yaml │ └── pvc.yaml ├── rcs │ └── rc.yaml ├── sd │ ├── jumpod.yaml │ ├── other-ns.yaml │ ├── other-rc.yaml │ ├── other-svc.yaml │ ├── rc.yaml │ └── svc.yaml ├── secrets │ └── pod.yaml ├── services │ ├── rc.yaml │ └── svc.yaml └── volumes │ └── pod.yaml ├── static └── img │ ├── Icon-Red_Hat-Media_and_documents-Paper_Stack_Blank-A-Black-RGB.png │ ├── kbe-logo.ico │ └── kbe-logo.png └── themes └── beautifulhugo ├── LICENSE ├── README.md ├── archetypes └── default.md ├── data └── beautifulhugo │ └── social.toml ├── i18n ├── en.yaml └── ja.yaml ├── layouts ├── 404.html ├── _default │ ├── baseof.html │ ├── list.html │ ├── single.html │ └── terms.html ├── index.html ├── page │ └── single.html ├── partials │ ├── disqus.html │ ├── footer.html │ ├── footer_custom.html │ ├── head.html │ ├── head_custom.html │ ├── header.html │ ├── nav.html │ ├── post_meta.html │ ├── script.html │ └── translation_link.html ├── post │ └── single.html └── shortcodes │ ├── figure.html │ ├── gallery.html │ ├── load-photoswipe-theme.html │ └── load-photoswipe.html ├── static ├── css │ ├── highlight.min.css │ ├── hugo-easy-gallery.css │ ├── main-minimal.css │ ├── main.css │ └── pygment_highlights.css ├── img │ ├── 404-southpark.jpg │ ├── avatar-favicon.png │ ├── avatar-icon.png │ ├── favicon.ico │ ├── favicon.ico.zip │ ├── hexagon-thumb.jpg │ ├── hexagon.jpg │ ├── path.jpg │ ├── sphere-thumb.jpg │ ├── sphere.jpg │ ├── triangle-thumb.jpg │ └── triangle.jpg └── js │ ├── bootstrap.js │ ├── bootstrap.min.js │ ├── highlight.min.js │ ├── jquery-1.11.2.min.js │ ├── load-photoswipe.js │ └── main.js └── theme.toml /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /public/ 3 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "public"] 2 | path = public 3 | url = git@github.com:openshift-evangelists/kbe.git 4 | branch = gh-pages 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Kubernetes By Example (KBE) 2 | 3 | This repository contains the source code for website [`kubernetesbyexample.com`](http://kubernetesbyexample.com) using [Hugo](https://gohugo.io) as the website engine. 4 | 5 | ## Contribute 6 | 7 | To contribute, please either raise an [issue](https://github.com/openshift-evangelists/kbe/issues) 8 | describing what you want to see covered here or send in a PR to the `main` branch. 9 | If you plan to contribute content, check out [content/page/](content/page/) 10 | for the content in Markdown and [specs/](specs/) for respective YAML specifications. 11 | 12 | ## Build locally 13 | 14 | 1. Install `hugo` following the installation [guide](https://gohugo.io/overview/installing) 15 | 1. Get your local preview by running following command in the top-level dir: 16 | 17 | ```bash 18 | hugo server --theme=beautifulhugo --buildDrafts 19 | ``` 20 | 21 | ## Publish 22 | 23 | For site admins only, requires push access to this repo. 24 | 25 | ### Setup 26 | 27 | After cloning the repo, add the `gh-pages` branch as a submodule under the `public` folder: 28 | 29 | ```bash 30 | git submodule add -f -b gh-pages git@github.com:openshift-evangelists/kbe.git public 31 | ``` 32 | 33 | ### Updates 34 | 35 | To update the live site with new content: 36 | 37 | ```bash 38 | # still in top-level dir build the content in public/ dir: 39 | hugo --theme=beautifulhugo 40 | # add generated content (which lives in the gh-pages branch): 41 | cd public/ 42 | git add --all 43 | git commit -m "release notes go here..." 44 | git push -f origin gh-pages 45 | ``` 46 | 47 | ## References 48 | 49 | - https://themes.gohugo.io/beautifulhugo/ 50 | - https://gohugo.io/overview/configuration/ 51 | - https://gohugo.io/overview/quickstart/ 52 | - https://gohugo.io/tutorials/github-pages-blog/ 53 | -------------------------------------------------------------------------------- /config.toml: -------------------------------------------------------------------------------- 1 | baseURL = "https://kubernetesbyexample.com" 2 | DefaultContentLanguage = "en" 3 | title = "Kubernetes By Example" 4 | theme = "beautifulhugo" 5 | metaDataFormat = "yaml" 6 | googleAnalytics = "UA-34425776-2" 7 | 8 | [Params] 9 | subtitle = "Brought to you by the OpenShift team" 10 | logo = "img/kbe-logo.png" 11 | favicon = "img/kbe-logo.ico" 12 | dateFormat = "2006-01-02" 13 | commit = false 14 | rss = false 15 | 16 | [[menu.main]] 17 | identifier = "try-out" 18 | name = "Try out!" 19 | url = "diy/" 20 | weight = "3" 21 | 22 | [[menu.main]] 23 | identifier = "contribute" 24 | name = "Contribute" 25 | url = "https://github.com/openshift-evangelists/kbe" 26 | weight = "2" 27 | 28 | [[menu.main]] 29 | identifier = "hands-on" 30 | name = "Hands-on Examples" 31 | url = "/" 32 | weight = "1" 33 | 34 | [[menu.main]] 35 | parent = "hands-on" 36 | name = "Pods" 37 | url = "pods/" 38 | weight = "1" 39 | 40 | [[menu.main]] 41 | parent = "hands-on" 42 | name = "Labels" 43 | url = "labels/" 44 | weight = "2" 45 | 46 | [[menu.main]] 47 | parent = "hands-on" 48 | name = "Deployments" 49 | url = "deployments/" 50 | weight = "3" 51 | 52 | [[menu.main]] 53 | parent = "hands-on" 54 | name = "Services" 55 | url = "services/" 56 | weight = "4" 57 | 58 | [[menu.main]] 59 | parent = "hands-on" 60 | name = "Service Discovery" 61 | url = "sd/" 62 | weight = "5" 63 | 64 | [[menu.main]] 65 | parent = "hands-on" 66 | name = "Port Forward" 67 | url = "pf/" 68 | weight = "6" 69 | 70 | [[menu.main]] 71 | parent = "hands-on" 72 | name = "Health Checks" 73 | url = "healthz/" 74 | weight = "7" 75 | 76 | [[menu.main]] 77 | parent = "hands-on" 78 | name = "Environment Variables" 79 | url = "envs/" 80 | weight = "8" 81 | 82 | [[menu.main]] 83 | parent = "hands-on" 84 | name = "Namespaces" 85 | url = "ns/" 86 | weight = "9" 87 | 88 | [[menu.main]] 89 | parent = "hands-on" 90 | name = "Volumes" 91 | url = "volumes/" 92 | weight = "10" 93 | 94 | [[menu.main]] 95 | parent = "hands-on" 96 | name = "Persistent Volumes" 97 | url = "pv/" 98 | weight = "11" 99 | 100 | [[menu.main]] 101 | parent = "hands-on" 102 | name = "Secrets" 103 | url = "secrets/" 104 | weight = "12" 105 | 106 | [[menu.main]] 107 | parent = "hands-on" 108 | name = "Logging" 109 | url = "logging/" 110 | weight = "13" 111 | 112 | [[menu.main]] 113 | parent = "hands-on" 114 | name = "Jobs" 115 | url = "jobs/" 116 | weight = "14" 117 | 118 | [[menu.main]] 119 | parent = "hands-on" 120 | name = "Stateful Sets" 121 | url = "statefulset/" 122 | weight = "15" 123 | 124 | [[menu.main]] 125 | parent = "hands-on" 126 | name = "Init Containers" 127 | url = "ic/" 128 | weight = "16" 129 | 130 | [[menu.main]] 131 | parent = "hands-on" 132 | name = "Nodes" 133 | url = "nodes/" 134 | weight = "17" 135 | 136 | [[menu.main]] 137 | parent = "hands-on" 138 | name = "API Server" 139 | url = "api/" 140 | weight = "18" -------------------------------------------------------------------------------- /content/_index.md: -------------------------------------------------------------------------------- 1 | This is a hands-on introduction to Kubernetes. Browse the examples: 2 | 3 | - [pods](/pods/) 4 | - [labels](/labels/) 5 | - [deployments](/deployments/) 6 | - [services](/services/) 7 | - [service discovery](/sd/) 8 | - [port forward](/pf/) 9 | - [health checks](/healthz/) 10 | - [environment variables](/envs/) 11 | - [namespaces](/ns/) 12 | - [volumes](/volumes/) 13 | - [persistent volumes](/pv/) 14 | - [secrets](/secrets/) 15 | - [logging](/logging/) 16 | - [jobs](/jobs/) 17 | - [stateful sets](/statefulset/) 18 | - [init containers](/ic/) 19 | - [nodes](/nodes/) 20 | - [API server](/api/) 21 | 22 | 23 | Want to try it out yourself? You can run all this on Red Hat's distribution of 24 | Kubernetes, OpenShift. Follow the instructions [here](/diy/) to quickly access an online environment. 25 | -------------------------------------------------------------------------------- /content/page/deployments.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = "Deployments" 3 | subtitle = "Kubernetes deployments by example" 4 | date = "2019-02-27" 5 | url = "/deployments/" 6 | +++ 7 | 8 | A deployment is a supervisor for [pods](/pods/), giving you fine-grained control over how and when a new pod version is rolled out as well as rolled back to a previous state. 9 | 10 | Let's create a [deployment](https://github.com/openshift-evangelists/kbe/blob/main/specs/deployments/d09.yaml) 11 | called `sise-deploy` that supervises two replicas of a pod as well as a replica set: 12 | 13 | ```bash 14 | kubectl apply -f https://raw.githubusercontent.com/openshift-evangelists/kbe/main/specs/deployments/d09.yaml 15 | ``` 16 | 17 | You can have a look at the deployment, as well as the the replica set and the pods the deployment looks after like so: 18 | 19 | ```bash 20 | kubectl get deploy 21 | ``` 22 | ```cat 23 | NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE 24 | sise-deploy 2 2 2 2 10s 25 | ``` 26 | 27 | ```bash 28 | kubectl get rs 29 | ``` 30 | ```cat 31 | NAME DESIRED CURRENT READY AGE 32 | sise-deploy-3513442901 2 2 2 19s 33 | ``` 34 | 35 | ```bash 36 | kubectl get pods 37 | ``` 38 | ```cat 39 | NAME READY STATUS RESTARTS AGE 40 | sise-deploy-3513442901-cndsx 1/1 Running 0 25s 41 | sise-deploy-3513442901-sn74v 1/1 Running 0 25s 42 | ``` 43 | 44 | Note the naming of the pods and replica set, derived from the deployment name. 45 | 46 | At this point in time the `sise` containers running in the pods are configured 47 | to return the version `0.9`. Let's verify this from within the cluster using `curl`: 48 | 49 | ```bash 50 | kubectl exec sise-deploy-3513442901-sn74v -t -- curl -s 127.0.0.1:9876/info 51 | ``` 52 | ```cat 53 | {"host": "127.0.0.1:9876", "version": "0.9", "from": "127.0.0.1"} 54 | ``` 55 | 56 | Let's now see what happens if we change that version to `1.0` in an updated 57 | [deployment](https://github.com/openshift-evangelists/kbe/blob/main/specs/deployments/d10.yaml): 58 | 59 | ```bash 60 | kubectl apply -f https://raw.githubusercontent.com/openshift-evangelists/kbe/main/specs/deployments/d10.yaml 61 | ``` 62 | ```cat 63 | deployment "sise-deploy" configured 64 | ``` 65 | 66 | Note that you could have used `kubectl edit deploy/sise-deploy` alternatively to 67 | achieve the same by manually editing the deployment. 68 | 69 | What we now see is the rollout of two new pods with the updated version `1.0` as well as the two old pods with version `0.9` being terminated: 70 | 71 | ```bash 72 | kubectl get pods 73 | ``` 74 | ```cat 75 | NAME READY STATUS RESTARTS AGE 76 | sise-deploy-2958877261-nfv28 1/1 Running 0 25s 77 | sise-deploy-2958877261-w024b 1/1 Running 0 25s 78 | sise-deploy-3513442901-cndsx 1/1 Terminating 0 16m 79 | sise-deploy-3513442901-sn74v 1/1 Terminating 0 16m 80 | ``` 81 | 82 | Also, a new replica set has been created by the deployment: 83 | 84 | ```bash 85 | kubectl get rs 86 | ``` 87 | ```cat 88 | NAME DESIRED CURRENT READY AGE 89 | sise-deploy-2958877261 2 2 2 4s 90 | sise-deploy-3513442901 0 0 0 24m 91 | ``` 92 | 93 | Note that during the deployment you can check the progress using `kubectl rollout status deploy/sise-deploy`. 94 | 95 | To verify that if the new `1.0` version is really available, we execute from within the cluster (again using `kubectl describe` get the IP of one of the pods): 96 | 97 | ```bash 98 | kubectl exec sise-deploy-2958877261-nfv28 -t -- curl -s 127.0.0.1:9876/info 99 | ``` 100 | ```cat 101 | {"host": "127.0.0.1:9876", "version": "1.0", "from": "127.0.0.1"} 102 | ``` 103 | 104 | A history of all deployments is available via: 105 | 106 | ```bash 107 | kubectl rollout history deploy/sise-deploy 108 | ``` 109 | ```cat 110 | deployments "sise-deploy" 111 | REVISION CHANGE-CAUSE 112 | 1 113 | 2 114 | ``` 115 | 116 | If there are problems in the deployment Kubernetes will automatically roll back to the previous version, however you can also explicitly roll back to a specific revision, as in our case to revision 1 (the original pod version): 117 | 118 | ```bash 119 | kubectl rollout undo deploy/sise-deploy --to-revision=1 120 | ``` 121 | ```cat 122 | deployment "sise-deploy" rolled back 123 | ``` 124 | ```bash 125 | kubectl rollout history deploy/sise-deploy 126 | ``` 127 | ```cat 128 | deployments "sise-deploy" 129 | REVISION CHANGE-CAUSE 130 | 2 131 | 3 132 | ``` 133 | ```bash 134 | kubectl get pods 135 | ``` 136 | ```cat 137 | NAME READY STATUS RESTARTS AGE 138 | sise-deploy-3513442901-ng8fz 1/1 Running 0 1m 139 | sise-deploy-3513442901-s8q4s 1/1 Running 0 1m 140 | ``` 141 | 142 | At this point in time we're back at where we started, with two new pods serving 143 | again version `0.9`. 144 | 145 | Finally, to clean up, we remove the deployment and with it the replica sets and 146 | pods it supervises: 147 | 148 | ```bash 149 | kubectl delete deploy sise-deploy 150 | ``` 151 | ```cat 152 | deployment "sise-deploy" deleted 153 | ``` 154 | 155 | See also the [docs](https://kubernetes.io/docs/concepts/workloads/controllers/deployment/) for more options on deployments and when they are triggered. 156 | 157 | [Previous](/labels) | [Next](/services) 158 | -------------------------------------------------------------------------------- /content/page/diy.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = "DIY" 3 | subtitle = "Try out for yourself" 4 | date = "2020-08-10" 5 | url = "/diy/" 6 | +++ 7 | 8 | If you want to try out the examples here yourself, here are a couple of options: 9 | 10 | ## OpenShift Playground 11 | You can use the OpenShift Playground to get access to a cluster for 60 minutes at a time. This option doesn't require you to install anything. This is the option we used for most of the examples on the site. 12 | 13 | [OpenShift Playground](https://learn.openshift.com/playgrounds/openshift45) 14 | 15 | Click the red *START SCENARIO* button, and then you will have access to an OpenShift cluster via a web-based terminal window with `kubectl` available. 16 | 17 | ## Try OpenShift 18 | Alternatively, you can head over to [openshift.com/try](https://openshift.com/try) for more ways to access or install an OpenShift cluster. For laptop installation see [CodeReadyContainers](https://code-ready.github.io/crc/). 19 | 20 | ## Other options 21 | Another option is to install `minikube` or `kind`. Minikube makes it easy to run a single-node Kubernetes cluster in a virtual machine, while Kind uses containers to help you run "Kubernetes IN Docker". 22 | 23 | [Minikube](https://kubernetes.io/docs/tasks/tools/install-minikube/) 24 | 25 | [Kind](https://kind.sigs.k8s.io/docs/user/quick-start/) 26 | -------------------------------------------------------------------------------- /content/page/envs.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = "Environment Variables" 3 | subtitle = "Kubernetes environment variables by example" 4 | date = "2019-02-27" 5 | url = "/envs/" 6 | +++ 7 | 8 | You can set environment variables for containers running in a pod and in addition, Kubernetes exposes certain runtime infos via environment variables 9 | automatically. 10 | 11 | Let's launch a [pod](https://github.com/openshift-evangelists/kbe/blob/main/specs/envs/pod.yaml) 12 | that we pass an environment variable `SIMPLE_SERVICE_VERSION` with the value `1.0`: 13 | 14 | ```bash 15 | kubectl apply -f https://raw.githubusercontent.com/openshift-evangelists/kbe/main/specs/envs/pod.yaml 16 | ``` 17 | 18 | Now, let's verify from within the cluster if the application running in the pod 19 | has picked up the environment variable `SIMPLE_SERVICE_VERSION`: 20 | 21 | ```bash 22 | kubectl exec envs -t -- curl -s 127.0.0.1:9876/info 23 | ``` 24 | ```cat 25 | {"host": "127.0.0.1:9876", "version": "1.0", "from": "127.0.0.1"} 26 | ``` 27 | 28 | And indeed it has picked up the user-provided environment variable since the default response would be `"version": "0.5.0"`. 29 | 30 | You can check what environment variables Kubernetes itself provides automatically 31 | (from within the cluster, using a dedicated endpoint that the [app](https://github.com/openshift-labs/simpleservice) 32 | exposes): 33 | ```bash 34 | kubectl exec envs -t -- curl -s 127.0.0.1:9876/env 35 | ``` 36 | ```cat 37 | {"version": "1.0", "env": "{'HOSTNAME': 'envs', 'DOCKER_REGISTRY_SERVICE_PORT': '5000', 'KUBERNETES_PORT_443_TCP_ADDR': '172.30.0.1', 'ROUTER_PORT_80_TCP_PROTO': 'tcp', 'KUBERNETES_PORT_53_UDP_PROTO': 'udp', 'ROUTER_SERVICE_HOST': '172.30.246.127', 'ROUTER_PORT_1936_TCP_PROTO': 'tcp', 'KUBERNETES_SERVICE_PORT_DNS': '53', 'DOCKER_REGISTRY_PORT_5000_TCP_PORT': '5000', 'PATH': '/usr/local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin', 'ROUTER_SERVICE_PORT_443_TCP': '443', 'KUBERNETES_PORT_53_TCP': 'tcp://172.30.0.1:53', 'KUBERNETES_SERVICE_PORT': '443', 'ROUTER_PORT_80_TCP_ADDR': '172.30.246.127', 'LANG': 'C.UTF-8', 'KUBERNETES_PORT_53_TCP_ADDR': '172.30.0.1', 'PYTHON_VERSION': '2.7.13', 'KUBERNETES_SERVICE_HOST': '172.30.0.1', 'PYTHON_PIP_VERSION': '9.0.1', 'DOCKER_REGISTRY_PORT_5000_TCP_PROTO': 'tcp', 'REFRESHED_AT': '2017-04-24T13:50', 'ROUTER_PORT_1936_TCP': 'tcp://172.30.246.127:1936', 'KUBERNETES_PORT_53_TCP_PROTO': 'tcp', 'KUBERNETES_PORT_53_TCP_PORT': '53', 'HOME': '/root', 'DOCKER_REGISTRY_SERVICE_HOST': '172.30.1.1', 'GPG_KEY': 'C01E1CAD5EA2C4F0B8E3571504C367C218ADD4FF', 'ROUTER_SERVICE_PORT_80_TCP': '80', 'ROUTER_PORT_443_TCP_ADDR': '172.30.246.127', 'ROUTER_PORT_1936_TCP_ADDR': '172.30.246.127', 'ROUTER_SERVICE_PORT': '80', 'ROUTER_PORT_443_TCP_PORT': '443', 'KUBERNETES_SERVICE_PORT_DNS_TCP': '53', 'KUBERNETES_PORT_53_UDP_ADDR': '172.30.0.1', 'KUBERNETES_PORT_53_UDP': 'udp://172.30.0.1:53', 'KUBERNETES_PORT': 'tcp://172.30.0.1:443', 'ROUTER_PORT_1936_TCP_PORT': '1936', 'ROUTER_PORT_80_TCP': 'tcp://172.30.246.127:80', 'KUBERNETES_SERVICE_PORT_HTTPS': '443', 'KUBERNETES_PORT_53_UDP_PORT': '53', 'ROUTER_PORT_80_TCP_PORT': '80', 'ROUTER_PORT': 'tcp://172.30.246.127:80', 'ROUTER_PORT_443_TCP': 'tcp://172.30.246.127:443', 'SIMPLE_SERVICE_VERSION': '1.0', 'ROUTER_PORT_443_TCP_PROTO': 'tcp', 'KUBERNETES_PORT_443_TCP': 'tcp://172.30.0.1:443', 'DOCKER_REGISTRY_PORT_5000_TCP': 'tcp://172.30.1.1:5000', 'DOCKER_REGISTRY_PORT': 'tcp://172.30.1.1:5000', 'KUBERNETES_PORT_443_TCP_PORT': '443', 'ROUTER_SERVICE_PORT_1936_TCP': '1936', 'DOCKER_REGISTRY_PORT_5000_TCP_ADDR': '172.30.1.1', 'DOCKER_REGISTRY_SERVICE_PORT_5000_TCP': '5000', 'KUBERNETES_PORT_443_TCP_PROTO': 'tcp'}"} 38 | ``` 39 | 40 | Alternatively, you can also use `kubectl exec` to display the environment variables within the container: 41 | 42 | ```bash 43 | kubectl exec envs -- printenv 44 | ``` 45 | ```cat 46 | PATH=/usr/local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin 47 | HOSTNAME=envs 48 | SIMPLE_SERVICE_VERSION=1.0 49 | KUBERNETES_PORT_53_UDP_ADDR=172.30.0.1 50 | KUBERNETES_PORT_53_TCP_PORT=53 51 | ROUTER_PORT_443_TCP_PROTO=tcp 52 | DOCKER_REGISTRY_PORT_5000_TCP_ADDR=172.30.1.1 53 | KUBERNETES_SERVICE_PORT_DNS_TCP=53 54 | ROUTER_PORT=tcp://172.30.246.127:80 55 | ... 56 | ``` 57 | 58 | Remove the `env` pod with: 59 | 60 | ```bash 61 | kubectl delete pod/envs 62 | ``` 63 | 64 | In addition to the above examples, you can also use `secrets`, volumes, or the [downward API](https://kubernetes.io/docs/user-guide/downward-api/) to inject additional information into your container environments. 65 | 66 | [Previous](/healthz) | [Next](/ns) 67 | -------------------------------------------------------------------------------- /content/page/healthz.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = "Health Checks" 3 | subtitle = "Kubernetes health checks by example" 4 | date = "2019-02-27" 5 | url = "/healthz/" 6 | +++ 7 | 8 | In order to verify if a container in a pod is healthy and ready to serve traffic, 9 | Kubernetes provides for a range of health checking mechanisms. Health checks, 10 | or **probes** as they are called in Kubernetes, are carried out 11 | by the [kubelet](https://kubernetes.io/docs/admin/kubelet/) to determine when to 12 | restart a container (for `livenessProbe`) and used by services and deployments to determine if a pod should receive traffic (for `readinessProbe`). 13 | 14 | We will focus on HTTP health checks in the following. Note that it is the responsibility 15 | of the application developer to expose a URL that the kubelet can 16 | use to determine if the container is healthy (and potentially ready). 17 | 18 | Let's create a [pod](https://github.com/openshift-evangelists/kbe/blob/main/specs/healthz/pod.yaml) 19 | that exposes an endpoint `/health`, responding with a HTTP `200` status code: 20 | 21 | ```bash 22 | kubectl apply -f https://raw.githubusercontent.com/openshift-evangelists/kbe/main/specs/healthz/pod.yaml 23 | ``` 24 | 25 | In the pod specification we've defined the following: 26 | 27 | ```cat 28 | livenessProbe: 29 | initialDelaySeconds: 2 30 | periodSeconds: 5 31 | httpGet: 32 | path: /health 33 | port: 9876 34 | ``` 35 | 36 | Above means that Kubernetes will start checking the `/health` endpoint, after initially waiting 2 seconds, every 5 seconds. 37 | 38 | If we now look at the pod we can see that it is considered healthy: 39 | 40 | ```bash 41 | kubectl describe pod hc 42 | ``` 43 | ```cat 44 | Name: hc 45 | Namespace: default 46 | Security Policy: anyuid 47 | Node: 192.168.99.100/192.168.99.100 48 | Start Time: Tue, 25 Apr 2017 16:21:11 +0100 49 | Labels: 50 | Status: Running 51 | ... 52 | Events: 53 | FirstSeen LastSeen Count From SubobjectPath Type Reason Message 54 | --------- -------- ----- ---- ------------- -------- ------ ------- 55 | 3s 3s 1 {default-scheduler } Normal Scheduled Successfully assigned hc to 192.168.99.100 56 | 3s 3s 1 {kubelet 192.168.99.100} spec.containers{sise} Normal Pulled Container image "quay.io/openshiftlabs/simpleservice:0.5.0" 57 | already present on machine 58 | 3s 3s 1 {kubelet 192.168.99.100} spec.containers{sise} Normal Created Created container with docker id 8a628578d6ad; Security:[seccomp=unconfined] 59 | 2s 2s 1 {kubelet 192.168.99.100} spec.containers{sise} Normal Started Started container with docker id 8a628578d6ad 60 | ``` 61 | 62 | Now we launch a [bad pod](https://github.com/openshift-evangelists/kbe/blob/main/specs/healthz/badpod.yaml), 63 | that is, a pod that has a container that randomly (in the time range 1 to 4 sec) 64 | does not return a 200 code: 65 | 66 | ```bash 67 | kubectl apply -f https://raw.githubusercontent.com/openshift-evangelists/kbe/main/specs/healthz/badpod.yaml 68 | ``` 69 | 70 | Looking at the events of the bad pod, we can see that the health check failed: 71 | 72 | ```bash 73 | kubectl describe pod badpod 74 | ``` 75 | ```cat 76 | ... 77 | Events: 78 | FirstSeen LastSeen Count From SubobjectPath Type Reason Message 79 | --------- -------- ----- ---- ------------- -------- ------ ------- 80 | 1m 1m 1 {default-scheduler } Normal Scheduled Successfully assigned badpod to 192.168.99.100 81 | 1m 1m 1 {kubelet 192.168.99.100} spec.containers{sise} Normal Created Created container with docker id 7dd660f04945; Security:[seccomp=unconfined] 82 | 1m 1m 1 {kubelet 192.168.99.100} spec.containers{sise} Normal Started Started container with docker id 7dd660f04945 83 | 1m 23s 2 {kubelet 192.168.99.100} spec.containers{sise} Normal Pulled Container image "quay.io/openshiftlabs/simpleservice:0.5.0" already present on machine 84 | 23s 23s 1 {kubelet 192.168.99.100} spec.containers{sise} Normal Killing Killing container with docker id 7dd660f04945: pod "badpod_default(53e5c06a-29cb-11e7-b44f-be3e8f4350ff)" container "sise" is unhealthy, it will be killed and re-created. 85 | 23s 23s 1 {kubelet 192.168.99.100} spec.containers{sise} Normal Created Created container with docker id ec63dc3edfaa; Security:[seccomp=unconfined] 86 | 23s 23s 1 {kubelet 192.168.99.100} spec.containers{sise} Normal Started Started container with docker id ec63dc3edfaa 87 | 1m 18s 4 {kubelet 192.168.99.100} spec.containers{sise} Warning Unhealthy Liveness probe failed: Get http://172.17.0.4:9876/health: net/http: request canceled (Client.Timeout exceeded while awaiting headers) 88 | ``` 89 | 90 | This can also be verified as follows: 91 | 92 | ```bash 93 | kubectl get pods 94 | ``` 95 | ```cat 96 | NAME READY STATUS RESTARTS AGE 97 | badpod 1/1 Running 4 2m 98 | hc 1/1 Running 0 6m 99 | ``` 100 | 101 | From above you can see that the `badpod` had already been re-launched 4 times, 102 | since the health check failed. 103 | 104 | In addition to a `livenessProbe` you can also specify a `readinessProbe`, which 105 | can be configured in the same way but has a different use case and semantics: 106 | it's used to check the start-up phase of a container in the pod. Imagine a container 107 | that loads some data from external storage such as S3 or a database that needs 108 | to initialize some tables. In this case you want to signal when the container is 109 | ready to serve traffic. 110 | 111 | Let's create a [pod](https://github.com/openshift-evangelists/kbe/blob/main/specs/healthz/ready.yaml) 112 | with a `readinessProbe` that kicks in after 10 seconds: 113 | 114 | ```bash 115 | kubectl apply -f https://raw.githubusercontent.com/openshift-evangelists/kbe/main/specs/healthz/ready.yaml 116 | ``` 117 | 118 | Looking at the events of the pod, we can see that, eventually, the pod is ready 119 | to serve traffic: 120 | 121 | ```bash 122 | kubectl describe pod ready 123 | ``` 124 | ```cat 125 | ... 126 | Conditions: [0/1888] 127 | Type Status 128 | Initialized True 129 | Ready True 130 | PodScheduled True 131 | ... 132 | ``` 133 | You can remove all the created pods with: 134 | 135 | ```bash 136 | kubectl delete pod/hc pod/ready pod/badpod 137 | ``` 138 | 139 | Learn more about configuring probes, including TCP and command probes, via the 140 | [docs](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-probes/). 141 | 142 | [Previous](/sd) | [Next](/envs) 143 | -------------------------------------------------------------------------------- /content/page/ic.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = "Init Containers" 3 | subtitle = "Kubernetes init containers by example" 4 | date = "2019-02-26" 5 | url = "/ic/" 6 | +++ 7 | 8 | It's sometimes necessary to prepare a container running in a pod. For example, you might want to wait for a service being available, want to configure things at runtime, or init some data in a database. In all of these cases, [init containers](https://kubernetes.io/docs/concepts/workloads/pods/init-containers/) are useful. Note that Kubernetes will execute all init containers (and they must all exit successfully) before the main container(s) are executed. 9 | 10 | So let's create an [deployment](https://github.com/openshift-evangelists/kbe/blob/main/specs/ic/deploy.yaml) consisting of an init container that writes a message into a file at `/ic/this` and the main (long-running) container reading out this file, then: 11 | 12 | ```bash 13 | kubectl apply -f https://raw.githubusercontent.com/openshift-evangelists/kbe/main/specs/ic/deploy.yaml 14 | ``` 15 | 16 | Now we can check the output of the main container: 17 | 18 | ```bash 19 | kubectl get deploy,po 20 | ``` 21 | ```cat 22 | NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE 23 | deployment.extensions/ic-deploy 1 1 1 1 11m 24 | 25 | NAME READY STATUS RESTARTS AGE 26 | pod/ic-deploy-bf75cbf87-8zmrb 1/1 Running 0 59s 27 | ``` 28 | ```bash 29 | kubectl logs ic-deploy-bf75cbf87-8zmrb -f 30 | ``` 31 | ```cat 32 | INIT_DONE 33 | INIT_DONE 34 | INIT_DONE 35 | INIT_DONE 36 | INIT_DONE 37 | ``` 38 | 39 | Send a break signal (Ctrl-C) when you're ready to disconnect from the log stream: 40 | ```bash 41 | ^C 42 | ``` 43 | 44 | Now we can cleanup after we are done as follows: 45 | 46 | ```bash 47 | kubectl delete -f https://raw.githubusercontent.com/openshift-evangelists/kbe/main/specs/ic/deploy.yaml 48 | ``` 49 | 50 | If you want to learn more about init containers and related topics, check out the blog post [Kubernetes: A Pod’s Life](https://blog.openshift.com/kubernetes-pods-life/). 51 | 52 | 53 | [Previous](/statefulset) | [Next](/nodes) 54 | -------------------------------------------------------------------------------- /content/page/jobs.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = "Jobs" 3 | subtitle = "Kubernetes jobs by example" 4 | date = "2020-03-19" 5 | url = "/jobs/" 6 | +++ 7 | 8 | A [job](https://kubernetes.io/docs/concepts/workloads/controllers/jobs-run-to-completion/) in Kubernetes is a supervisor for pods carrying out batch processes, that is, 9 | a process that runs for a certain time to completion, for example a calculation 10 | or a backup operation. 11 | 12 | Let's create a [job](https://github.com/openshift-evangelists/kbe/blob/main/specs/jobs/job.yaml) 13 | called `countdown` that supervises a pod counting from 9 down to 1: 14 | 15 | ```bash 16 | kubectl apply -f https://raw.githubusercontent.com/openshift-evangelists/kbe/main/specs/jobs/job.yaml 17 | ``` 18 | 19 | You can see the job and the pod it looks after like so: 20 | 21 | ```bash 22 | kubectl get jobs 23 | ``` 24 | ```cat 25 | NAME DESIRED SUCCESSFUL AGE 26 | countdown 1 1 5s 27 | ``` 28 | 29 | ```bash 30 | kubectl get pods 31 | ``` 32 | ```cat 33 | NAME READY STATUS RESTARTS AGE 34 | countdown-qkjx8 0/1 Completed 0 2m17s 35 | ``` 36 | 37 | To learn more about the status of the job, do: 38 | 39 | ```bash 40 | kubectl describe jobs/countdown 41 | ``` 42 | ```cat 43 | Name: countdown 44 | Namespace: default 45 | Selector: controller-uid=4960c8be-6a3f-11ea-84fd-0242ac11002a 46 | Labels: controller-uid=4960c8be-6a3f-11ea-84fd-0242ac11002a 47 | job-name=countdown 48 | Annotations: kubectl.kubernetes.io/last-applied-configuration: 49 | {"apiVersion":"batch/v1","kind":"Job","metadata":{"annotations":{},"name":"countdown","namespace":"default"},"spec":{"template":{"metadata... 50 | Parallelism: 1 51 | Completions: 1 52 | Start Time: Fri, 20 Mar 2020 00:11:03 +0000 53 | Completed At: Fri, 20 Mar 2020 00:11:12 +0000 54 | Duration: 9s 55 | Pods Statuses: 0 Running / 1 Succeeded / 0 Failed 56 | Pod Template: 57 | Labels: controller-uid=4960c8be-6a3f-11ea-84fd-0242ac11002a 58 | job-name=countdown 59 | Containers: 60 | counter: 61 | Image: centos:7 62 | Port: 63 | Host Port: 64 | Command: 65 | bin/bash 66 | -c 67 | for i in 9 8 7 6 5 4 3 2 1 ; do echo $i ; done 68 | Environment: 69 | Mounts: 70 | Volumes: 71 | Events: 72 | Type Reason Age From Message 73 | ---- ------ ---- ---- ------- 74 | Normal SuccessfulCreate 3m18s job-controller Created pod: countdown-qkjx8 75 | ``` 76 | 77 | And to see the output of the job via the pod it supervised, execute: 78 | 79 | ```bash 80 | kubectl logs countdown-qkjx8 81 | ``` 82 | ```cat 83 | 9 84 | 8 85 | 7 86 | 6 87 | 5 88 | 4 89 | 3 90 | 2 91 | 1 92 | ``` 93 | 94 | To clean up, use the `delete` verb on the job object which will remove all the 95 | supervised pods: 96 | 97 | ```bash 98 | kubectl delete job countdown 99 | ``` 100 | ```cat 101 | job "countdown" deleted 102 | ``` 103 | 104 | Note that there are also more advanced ways to use jobs, for example, 105 | by utilizing a [work queue](https://kubernetes.io/docs/tasks/job/coarse-parallel-processing-work-queue/) 106 | or scheduling the execution at a certain time via [cron jobs](https://kubernetes.io/docs/concepts/workloads/controllers/cron-jobs/). 107 | 108 | [Previous](/logging) | [Next](/statefulset) 109 | -------------------------------------------------------------------------------- /content/page/labels.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = "Labels" 3 | subtitle = "Kubernetes labels by example" 4 | date = "2019-02-27" 5 | url = "/labels/" 6 | +++ 7 | 8 | Labels are the mechanism you use to organize Kubernetes objects. A label is a key-value 9 | pair with certain [restrictions](https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#syntax-and-character-set) 10 | concerning length and allowed values but without any pre-defined meaning. 11 | So you're free to choose labels as you see fit, for example, to express 12 | environments such as 'this pod is running in production' or ownership, 13 | like 'department X owns that pod'. 14 | 15 | Let's create a [pod](https://github.com/openshift-evangelists/kbe/blob/main/specs/labels/pod.yaml) that initially has one label (`env=development`): 16 | 17 | 18 | ```bash 19 | kubectl apply -f https://raw.githubusercontent.com/openshift-evangelists/kbe/main/specs/labels/pod.yaml 20 | ``` 21 | ```bash 22 | kubectl get pods --show-labels 23 | ``` 24 | ```cat 25 | NAME READY STATUS RESTARTS AGE LABELS 26 | labelex 1/1 Running 0 10m env=development 27 | ``` 28 | In above `get pods` command note the `--show-labels` option that output the 29 | labels of an object in an additional column. 30 | 31 | You can add a label to the pod as: 32 | 33 | ```bash 34 | kubectl label pods labelex owner=michael 35 | ``` 36 | ```bash 37 | kubectl get pods --show-labels 38 | ``` 39 | ```cat 40 | NAME READY STATUS RESTARTS AGE LABELS 41 | labelex 1/1 Running 0 16m env=development,owner=michael 42 | ``` 43 | 44 | To use a label for filtering, for example to list only pods that have an 45 | `owner` that equals `michael`, use the `--selector` option: 46 | 47 | ```bash 48 | kubectl get pods --selector owner=michael 49 | ``` 50 | ```cat 51 | NAME READY STATUS RESTARTS AGE 52 | labelex 1/1 Running 0 27m 53 | ``` 54 | 55 | The `--selector` option can be abbreviated to `-l`, so to select pods that are 56 | labelled with `env=development`, do: 57 | 58 | ```bash 59 | kubectl get pods -l env=development 60 | ``` 61 | ```cat 62 | NAME READY STATUS RESTARTS AGE 63 | labelex 1/1 Running 0 27m 64 | ``` 65 | 66 | Oftentimes, Kubernetes objects also support set-based selectors. 67 | Let's launch [another pod](https://github.com/openshift-evangelists/kbe/blob/main/specs/labels/anotherpod.yaml) 68 | that has two labels (`env=production` and `owner=michael`): 69 | 70 | ```bash 71 | kubectl apply -f https://raw.githubusercontent.com/openshift-evangelists/kbe/main/specs/labels/anotherpod.yaml 72 | ``` 73 | 74 | Now, let's list all pods that are either labelled with `env=development` or with 75 | `env=production`: 76 | 77 | ```bash 78 | kubectl get pods -l 'env in (production, development)' 79 | ``` 80 | ```cat 81 | NAME READY STATUS RESTARTS AGE 82 | labelex 1/1 Running 0 43m 83 | labelexother 1/1 Running 0 3m 84 | ``` 85 | 86 | Other verbs also support label selection, for example, you could 87 | remove both of these pods with: 88 | 89 | ```bash 90 | kubectl delete pods -l 'env in (production, development)' 91 | ``` 92 | 93 | Beware that this will destroy any pods with those labels. 94 | 95 | You can also delete them directly, via their names, with: 96 | 97 | ```bash 98 | kubectl delete pods labelex 99 | ``` 100 | ```bash 101 | kubectl delete pods labelexother 102 | ``` 103 | 104 | Note that labels are not restricted to pods. In fact you can apply them to 105 | all sorts of objects, such as nodes or services. 106 | 107 | [Previous](/pods) | [Next](/deployments) 108 | -------------------------------------------------------------------------------- /content/page/logging.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = "Logging" 3 | subtitle = "Kubernetes logging by example" 4 | date = "2019-02-28" 5 | url = "/logging/" 6 | +++ 7 | 8 | Logging is one option to understand what is going on inside your applications 9 | and the cluster at large. Basic logging in Kubernetes makes the output a 10 | container produces available, which is a good use case for debugging. More advanced 11 | [setups](https://kubernetes.io/docs/concepts/cluster-administration/logging/) consider logs across nodes and store 12 | them in a central place, either within the cluster or via a dedicated (cloud-based) service. 13 | 14 | Let's create a [pod](https://github.com/openshift-evangelists/kbe/blob/main/specs/logging/pod.yaml) 15 | called `logme` that runs a container writing to `stdout` and `stderr`: 16 | 17 | ```bash 18 | kubectl apply -f https://raw.githubusercontent.com/openshift-evangelists/kbe/main/specs/logging/pod.yaml 19 | ``` 20 | 21 | To view the five most recent log lines of the `gen` container in the `logme` pod, 22 | execute: 23 | 24 | ```bash 25 | kubectl logs --tail=5 logme -c gen 26 | ``` 27 | ```cat 28 | Thu Apr 27 11:34:40 UTC 2017 29 | Thu Apr 27 11:34:41 UTC 2017 30 | Thu Apr 27 11:34:41 UTC 2017 31 | Thu Apr 27 11:34:42 UTC 2017 32 | Thu Apr 27 11:34:42 UTC 2017 33 | ``` 34 | 35 | To stream the log of the `gen` container in the `logme` pod (like `tail -f`), do: 36 | 37 | ```bash 38 | kubectl logs -f --since=10s logme -c gen 39 | ``` 40 | ```cat 41 | Thu Apr 27 11:43:11 UTC 2017 42 | Thu Apr 27 11:43:11 UTC 2017 43 | Thu Apr 27 11:43:12 UTC 2017 44 | Thu Apr 27 11:43:12 UTC 2017 45 | Thu Apr 27 11:43:13 UTC 2017 46 | ... 47 | ``` 48 | 49 | Note that if you wouldn't have specified `--since=10s` in the above command, you 50 | would have gotten all log lines from the start of the container. 51 | 52 | You can also view logs of pods that have already completed their lifecycle. 53 | For this we create a [pod](https://github.com/openshift-evangelists/kbe/blob/main/specs/logging/oneshotpod.yaml) 54 | called `oneshot` that counts down from 9 to 1 and then exits. Using the `-p` option 55 | you can print the logs for previous instances of the container in a pod: 56 | 57 | ```bash 58 | kubectl apply -f https://raw.githubusercontent.com/openshift-evangelists/kbe/main/specs/logging/oneshotpod.yaml 59 | ``` 60 | ```bash 61 | kubectl logs -p oneshot -c gen 62 | ``` 63 | ```cat 64 | 9 65 | 8 66 | 7 67 | 6 68 | 5 69 | 4 70 | 3 71 | 2 72 | 1 73 | ``` 74 | 75 | You can remove the created pods with: 76 | 77 | ```bash 78 | kubectl delete pod/logme pod/oneshot 79 | ``` 80 | 81 | [Previous](/secrets) | [Next](/jobs) 82 | -------------------------------------------------------------------------------- /content/page/nodes.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = "Nodes" 3 | subtitle = "Kubernetes nodes by example" 4 | date = "2020-03-23" 5 | url = "/nodes/" 6 | +++ 7 | 8 | In Kubernetes, nodes are the (virtual) machines where your workloads in shape of pods run. As a developer you typically don't deal with nodes directly, however as an admin 9 | you might want to familiarize yourself with node [operations](https://kubernetes.io/docs/concepts/nodes/node/). 10 | 11 | To list available nodes in your cluster (note that the output will depend on the environment 12 | you're using. This example is using the [OpenShift Playground](/diy/)): 13 | 14 | ```bash 15 | kubectl get nodes 16 | ``` 17 | ```cat 18 | NAME STATUS ROLES AGE VERSION 19 | crc-rk2fc-master-0 Ready master,worker 102d v1.14.6+888f9c630 20 | ``` 21 | 22 | One interesting task, from a developer point of view, is to make Kubernetes 23 | schedule a pod on a certain node. For this, we first need to label the node 24 | we want to target: 25 | 26 | ```bash 27 | kubectl label nodes crc-rk2fc-master-0 shouldrun=here 28 | ``` 29 | ```cat 30 | node/crc-rk2fc-master-0 labeled 31 | ``` 32 | 33 | Now we can create a [pod](https://github.com/openshift-evangelists/kbe/blob/main/specs/nodes/pod.yaml) 34 | that gets scheduled on the node with the label `shouldrun=here`: 35 | 36 | ```bash 37 | kubectl apply -f https://raw.githubusercontent.com/openshift-evangelists/kbe/main/specs/nodes/pod.yaml 38 | ``` 39 | ```bash 40 | kubectl get pods --output=wide 41 | ``` 42 | ```cat 43 | NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES 44 | onspecificnode 1/1 Running 0 2m31s 10.128.1.11 crc-rk2fc-master-0 45 | ``` 46 | 47 | To learn more about a specific node, `crc-rk2fc-master-0` in our case, do: 48 | 49 | ```bash 50 | kubectl describe node crc-rk2fc-master-0 51 | ``` 52 | ```cat 53 | Name: crc-rk2fc-master-0 54 | Roles: master,worker 55 | Labels: beta.kubernetes.io/arch=amd64 56 | beta.kubernetes.io/os=linux 57 | kubernetes.io/arch=amd64 58 | kubernetes.io/hostname=crc-rk2fc-master-0 59 | kubernetes.io/os=linux 60 | node-role.kubernetes.io/master= 61 | node-role.kubernetes.io/worker= 62 | node.openshift.io/os_id=rhcos 63 | shouldrun=here 64 | Annotations: machine.openshift.io/machine: openshift-machine-api/crc-rk2fc-master-0 65 | machineconfiguration.openshift.io/currentConfig: rendered-master-757d2d73a4ba859a3508c78070169043 66 | machineconfiguration.openshift.io/desiredConfig: rendered-master-757d2d73a4ba859a3508c78070169043 67 | machineconfiguration.openshift.io/reason: 68 | machineconfiguration.openshift.io/ssh: accessed 69 | machineconfiguration.openshift.io/state: Done 70 | volumes.kubernetes.io/controller-managed-attach-detach: true 71 | CreationTimestamp: Thu, 12 Dec 2019 06:47:09 +0000 72 | Taints: 73 | Unschedulable: false 74 | Conditions: 75 | Type Status LastHeartbeatTime LastTransitionTime Reason Message 76 | ---- ------ ----------------- ------------------ ------ ------- 77 | MemoryPressure False Mon, 23 Mar 2020 18:20:39 +0000 Thu, 12 Dec 2019 06:47:08 +0000 KubeletHasSufficientMemory kubelet has sufficient memoryavailable 78 | DiskPressure False Mon, 23 Mar 2020 18:20:39 +0000 Thu, 12 Dec 2019 06:47:08 +0000 KubeletHasNoDiskPressure kubelet has no disk pressure 79 | PIDPressure False Mon, 23 Mar 2020 18:20:39 +0000 Thu, 12 Dec 2019 06:47:08 +0000 KubeletHasSufficientPID kubelet has sufficient PID available 80 | Ready True Mon, 23 Mar 2020 18:20:39 +0000 Mon, 23 Mar 2020 17:56:48 +0000 KubeletReady kubelet is posting ready status 81 | Addresses: 82 | InternalIP: 172.17.0.23 83 | Hostname: crc-rk2fc-master-0 84 | Capacity: 85 | cpu: 6 86 | hugepages-1Gi: 0 87 | hugepages-2Mi: 0 88 | memory: 16033876Ki 89 | pods: 250 90 | Allocatable: 91 | cpu: 5500m 92 | hugepages-1Gi: 0 93 | hugepages-2Mi: 0 94 | memory: 15419476Ki 95 | pods: 250 96 | System Info: 97 | Machine ID: 33c8de1eb5364c94b5e215e58eef30ac 98 | System UUID: 33c8de1eb5364c94b5e215e58eef30ac 99 | Boot ID: f51f55d7-7702-48bc-bbc0-d68372e0fbf1 100 | Kernel Version: 4.18.0-147.0.3.el8_1.x86_64 101 | OS Image: Red Hat Enterprise Linux CoreOS 42.81.20191203.0 (Ootpa) 102 | Operating System: linux 103 | Architecture: amd64 104 | Container Runtime Version: cri-o://1.14.11-0.24.dev.rhaos4.2.gitc41de67.el8 105 | Kubelet Version: v1.14.6+888f9c630 106 | Kube-Proxy Version: v1.14.6+888f9c630 107 | Non-terminated Pods: (46 in total) 108 | Namespace Name CPU Requests CPU Limits Memory Requests Memory Limits AGE 109 | --------- ---- ------------ ---------- --------------- ------------- --- 110 | default onspecificnode 0 (0%) 0 (0%) 0 (0%) 0 (0%) 3m2s 111 | openshift-apiserver 112 | ... 113 | Allocated resources: 114 | (Total limits may be over 100 percent, i.e., overcommitted.) 115 | Resource Requests Limits 116 | -------- -------- ------ 117 | cpu 3010m (54%) 700m (12%) 118 | memory 8289Mi (55%) 687Mi (4%) 119 | ephemeral-storage 0 (0%) 0 (0%) 120 | ``` 121 | 122 | Note that there are more sophisticated methods than shown above, such as using affinity, to [assign pods to nodes](https://kubernetes.io/docs/concepts/configuration/assign-pod-node/) and depending on your use case, you might want to check those out as well. 123 | 124 | [Previous](/ic) | [Next](/api) 125 | -------------------------------------------------------------------------------- /content/page/ns.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = "Namespaces" 3 | subtitle = "Kubernetes namespaces by example" 4 | date = "2020-03-23" 5 | url = "/ns/" 6 | +++ 7 | 8 | Namespaces provide a scope for Kubernetes resources, carving up your cluster in smaller units. You can think of it 9 | as a workspace you're sharing with other users. Many resources such as pods and 10 | services are namespaced, while some, for example, nodes are not namespaced (but cluster-wide). As a developer you'd usually use an assigned namespace, however admins may wish to manage them, for example to set up access control or resource quotas. 11 | 12 | Let's list all namespaces (note that the output will depend on the environment 13 | you're using, we're using the [OpenShift Playground](/diy/) here): 14 | 15 | ```bash 16 | kubectl get ns 17 | ``` 18 | ```cat 19 | NAME STATUS AGE 20 | default Active 98d 21 | kube-node-lease Active 98d 22 | kube-public Active 98d 23 | kube-system Active 98d 24 | openshift Active 98d 25 | openshift-apiserver Active 98d 26 | ... 27 | ``` 28 | 29 | You can learn more about a namespace using the `describe` verb, for example: 30 | 31 | ```bash 32 | kubectl describe ns default 33 | ``` 34 | ```cat 35 | Name: default 36 | Labels: 37 | Status: Active 38 | 39 | No resource quota. 40 | 41 | No resource limits. 42 | ``` 43 | 44 | Let's now create a new [namespace](https://github.com/openshift-evangelists/kbe/blob/main/specs/ns/ns.yaml) 45 | called `test` now: 46 | 47 | ```bash 48 | kubectl apply -f https://raw.githubusercontent.com/openshift-evangelists/kbe/main/specs/ns/ns.yaml 49 | ``` 50 | ```cat 51 | namespace "test" created 52 | ``` 53 | ```bash 54 | kubectl get ns 55 | ``` 56 | ```cat 57 | NAME STATUS AGE 58 | default Active 98d 59 | kube-node-lease Active 98d 60 | kube-public Active 98d 61 | kube-system Active 98d 62 | openshift Active 98d 63 | openshift-apiserver Active 98d 64 | ... 65 | test Active 16s 66 | ``` 67 | 68 | Alternatively, we could have created the namespace using the `kubectl create namespace test` command. 69 | 70 | To launch a [pod](https://github.com/openshift-evangelists/kbe/blob/main/specs/ns/pod.yaml) in 71 | the newly created namespace `test`, do: 72 | 73 | ```bash 74 | kubectl apply --namespace=test -f https://raw.githubusercontent.com/openshift-evangelists/kbe/main/specs/ns/pod.yaml 75 | ``` 76 | 77 | Note that using above method the namespace becomes a runtime property, that is, 78 | you can deploy the same pod or service, etc. into multiple 79 | namespaces (for example: `dev` and `prod`). Hard-coding the 80 | namespace directly in the `metadata` section like shown in the following is possible but causes less flexibility when deploying your apps: 81 | 82 | ```cat 83 | apiVersion: v1 84 | kind: Pod 85 | metadata: 86 | name: podintest 87 | namespace: test 88 | ``` 89 | 90 | To list namespaced objects such as our pod `podintest`, run the following command: 91 | 92 | ```bash 93 | kubectl get pods --namespace=test 94 | ``` 95 | ```cat 96 | NAME READY STATUS RESTARTS AGE 97 | podintest 1/1 Running 0 16s 98 | ``` 99 | 100 | You can remove the namespace (and everything inside) with: 101 | 102 | ```bash 103 | kubectl delete ns test 104 | ``` 105 | 106 | If you're an admin, you might want to check out the [docs](https://kubernetes.io/docs/tasks/administer-cluster/namespaces/) 107 | for more info how to handle namespaces. 108 | 109 | [Previous](/envs) | [Next](/volumes) 110 | -------------------------------------------------------------------------------- /content/page/pf.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = "Port Forward" 3 | subtitle = "Kubernetes port forward by example" 4 | date = "2019-02-12" 5 | url = "/pf/" 6 | +++ 7 | 8 | In the context of developing apps on Kubernetes it is often useful to quickly access a service from your local environment without exposing it using, for example, a load balancer or an ingress resource. In this case you can use [port forwarding](https://kubernetes.io/docs/tasks/access-application-cluster/port-forward-access-application-cluster/). 9 | 10 | Let's create an [app](https://github.com/openshift-evangelists/kbe/blob/main/specs/pf/app.yaml) consisting of a deployment and a service called `simpleservice`, serving on port `80`: 11 | 12 | ```bash 13 | kubectl apply -f https://raw.githubusercontent.com/openshift-evangelists/kbe/main/specs/pf/app.yaml 14 | ``` 15 | 16 | Let's say we want to access the `simpleservice` service from the local environment, say, your laptop, on port `8080`. So we forward the traffic as follows: 17 | 18 | ```bash 19 | kubectl port-forward service/simpleservice 8080:80 20 | ``` 21 | ```cat 22 | Forwarding from 127.0.0.1:8080 -> 9876 23 | Forwarding from [::1]:8080 -> 9876 24 | ``` 25 | 26 | We can see from above that the traffic gets eventually routed through the service to the pod serving on port `9876`. 27 | 28 | Now we can invoke the service locally like so (using a separate terminal session): 29 | 30 | ```bash 31 | curl localhost:8080/info 32 | ``` 33 | ```cat 34 | {"host": "localhost:8080", "version": "0.5.0", "from": "127.0.0.1"} 35 | ``` 36 | 37 | Remember that port forwarding is not meant for production traffic but for development and experimentation. 38 | 39 | You can remove the `simpleservice` with 40 | 41 | ```bash 42 | kubectl delete service/simpleservice 43 | ``` 44 | ```bash 45 | kubectl delete deployment sise-deploy 46 | ``` 47 | 48 | [Previous](/sd) | [Next](/healthz) 49 | -------------------------------------------------------------------------------- /content/page/pods.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = "Pods" 3 | subtitle = "Kubernetes pods by example" 4 | date = "2019-03-23" 5 | url = "/pods/" 6 | +++ 7 | 8 | A pod is a collection of containers sharing a network and mount namespace 9 | and is the basic unit of deployment in Kubernetes. All containers in a pod 10 | are scheduled on the same node. 11 | 12 | To launch a pod using the container [image](https://quay.io/repository/openshiftlabs/simpleservice/) 13 | `quay.io/openshiftlabs/simpleservice:0.5.0` and exposing a HTTP API on port `9876`, execute: 14 | 15 | ```bash 16 | kubectl run sise --image=quay.io/openshiftlabs/simpleservice:0.5.0 --port=9876 17 | ``` 18 | ```cat 19 | pod/sise created 20 | ``` 21 | 22 | ***Note: Deprecation Warning!*** 23 | Older releases of `kubectl` will produce a [deployment](/deployments/) resource as the result of the provided `kubectl run` example, while newer releases produce a single `pod` resource. The example commands in this section should still work (assuming you substitute your own pod name) - but you'll need to run `kubectl delete deployment sise` at the end of this section to clean up. 24 | 25 | Check to see if the pod is running: 26 | 27 | ```bash 28 | kubectl get pods 29 | ``` 30 | ```cat 31 | NAME READY STATUS RESTARTS AGE 32 | sise 1/1 Running 0 1m 33 | ``` 34 | 35 | If the above output returns a longer pod name, make sure to use it in the following examples (in place of `sise`) 36 | 37 | This container image happens to include a copy of `curl`, which provides an additional way to verify that the primary webservice process is responding (over the local net at least): 38 | 39 | ```bash 40 | kubectl exec sise -t -- curl -s localhost:9876/info 41 | ``` 42 | ```cat 43 | {"host": "localhost:9876", "version": "0.5.0", "from": "127.0.0.1"} 44 | ``` 45 | 46 | From within the cluster (e.g. via `kubectl exec` or [`oc rsh`](https://docs.openshift.com/container-platform/latest/cli_reference/openshift_cli/developer-cli-commands.html#rsh)) this pod will also be directly accessible via it's associated pod IP `172.17.0.3` 47 | 48 | ```bash 49 | kubectl describe pod sise | grep IP: 50 | ``` 51 | ```cat 52 | IP: 172.17.0.3 53 | ``` 54 | 55 | The kubernetes proxy API provides an additional opportunity to make external connections to pods within the cluster using `curl`: 56 | ```bash 57 | export K8S_API="https://$(kubectl config get-clusters | tail -n 1)" 58 | export API_TOKEN="$(kubectl config view -o jsonpath={.users[-1].user.token})" 59 | export NAMESPACE="default" 60 | export PODNAME="sise" 61 | curl -s -k -H"Authorization: Bearer $API_TOKEN" \ 62 | $K8S_API/api/v1/namespaces/$NAMESPACE/pods/$PODNAME/proxy/info 63 | ``` 64 | 65 | Cleanup: 66 | ```bash 67 | kubectl delete pod,deployment sise 68 | ``` 69 | 70 | #### Using configuration file 71 | 72 | You can also create a pod from a configuration file. 73 | In this case the [pod](https://github.com/openshift-evangelists/kbe/blob/main/specs/pods/pod.yaml) is 74 | running the already known `simpleservice` image from above along with 75 | a generic `CentOS` container: 76 | 77 | ```bash 78 | kubectl apply -f https://raw.githubusercontent.com/openshift-evangelists/kbe/main/specs/pods/pod.yaml 79 | ``` 80 | ```cat 81 | pod/twocontainers created 82 | ``` 83 | 84 | ```bash 85 | kubectl get pods 86 | ``` 87 | ```cat 88 | NAME READY STATUS RESTARTS AGE 89 | twocontainers 2/2 Running 0 7s 90 | ``` 91 | 92 | Containers that share a pod are able to communicate using local networking. 93 | 94 | This example demonstrates how to exec into a sidecar `shell` container to access and inspect the `sise` container via `localhost`: 95 | 96 | ```bash 97 | kubectl exec twocontainers -t -- curl -s localhost:9876/info 98 | ``` 99 | ```cat 100 | {"host": "localhost:9876", "version": "0.5.0", "from": "127.0.0.1"} 101 | ``` 102 | 103 | Define the `resources` attribute to influence how much CPU and/or RAM a 104 | container in a [pod](https://github.com/openshift-evangelists/kbe/blob/main/specs/pods/constraint-pod.yaml) can use (here: `64MB` of RAM and `0.5` CPUs): 105 | 106 | ```bash 107 | kubectl create -f https://raw.githubusercontent.com/openshift-evangelists/kbe/main/specs/pods/constraint-pod.yaml 108 | ``` 109 | ```cat 110 | pod/constraintpod created 111 | ``` 112 | 113 | ```bash 114 | kubectl describe pod constraintpod 115 | ``` 116 | ```cat 117 | ... 118 | Containers: 119 | sise: 120 | ... 121 | Limits: 122 | cpu: 500m 123 | memory: 64Mi 124 | Requests: 125 | cpu: 500m 126 | memory: 64Mi 127 | ... 128 | ``` 129 | 130 | Learn more about resource constraints in Kubernetes via the docs [here](https://kubernetes.io/docs/tasks/configure-pod-container/assign-cpu-ram-container/) 131 | and [here](https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/). 132 | 133 | To clean up and remove all the remaining pods, try: 134 | 135 | ```bash 136 | kubectl delete pod twocontainers 137 | ``` 138 | ```bash 139 | kubectl delete pod constraintpod 140 | ``` 141 | 142 | To sum up, launching one or more containers (together) in Kubernetes is simple, 143 | however doing it directly as shown above comes with a serious limitation: you have to 144 | manually take care of keeping them running in case of a failure. A better way 145 | to supervise pods is to use [deployments](/deployments), giving you much more control over the life cycle, including rolling out a new version. 146 | 147 | [Next](/labels) 148 | -------------------------------------------------------------------------------- /content/page/proxy.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = "API Server access" 3 | subtitle = "Kubernetes API Server access by example" 4 | date = "2019-03-23" 5 | url = "/api/" 6 | +++ 7 | 8 | Sometimes it's useful or necessary to directly [access the Kubernetes API server](https://kubernetes.io/docs/tasks/access-kubernetes-api/http-proxy-access-api/), for exploratory or testing purposes. 9 | 10 | In order to do this, one option is to proxy the API to your local environment, using: 11 | 12 | ```bash 13 | kubectl proxy --port=8080 14 | ``` 15 | ```cat 16 | Starting to serve on 127.0.0.1:8080 17 | 18 | ``` 19 | 20 | Now you can query the API (in a separate terminal session) like so: 21 | 22 | ```bash 23 | curl http://localhost:8080/api/v1 24 | ``` 25 | ```cat 26 | { 27 | "kind": "APIResourceList", 28 | "groupVersion": "v1", 29 | "resources": [ 30 | { 31 | ... 32 | { 33 | "name": "services/status", 34 | "singularName": "", 35 | "namespaced": true, 36 | "kind": "Service", 37 | "verbs": [ 38 | "get", 39 | "patch", 40 | "update" 41 | ] 42 | } 43 | ] 44 | } 45 | ``` 46 | 47 | Alternatively, without proxying, you can use `kubectl` directly as follows to achieve the same: 48 | 49 | ```bash 50 | kubectl get --raw /api/v1 51 | ``` 52 | 53 | Further, if you want to explore the supported API versions and/or resources, you can use the following commands: 54 | 55 | ```bash 56 | kubectl api-versions 57 | ``` 58 | ```cat 59 | admissionregistration.k8s.io/v1beta1 60 | ... 61 | v1 62 | ``` 63 | ```bash 64 | kubectl api-resources 65 | ``` 66 | ```cat 67 | NAME SHORTNAMES APIGROUP NAMESPACED KIND 68 | bindings true Binding 69 | componentstatuses cs false ComponentStatus 70 | configmaps cm true ConfigMap 71 | ... 72 | ``` 73 | 74 | [Previous](/nodes) 75 | -------------------------------------------------------------------------------- /content/page/pv.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = "Persistent Volumes" 3 | subtitle = "Kubernetes persistent volumes by example" 4 | date = "2019-02-27" 5 | url = "/pv/" 6 | +++ 7 | 8 | A [persistent volume](https://kubernetes.io/docs/concepts/storage/persistent-volumes/) (PV) is a cluster-wide resource that you can use to store data in a way that it persists beyond the lifetime of a pod. The PV is not backed by locally-attached storage on a worker node but by networked storage system such as EBS or NFS or a distributed filesystem like Ceph. 9 | 10 | If you are using 11 | [OpenShift Playground](https://learn.openshift.com/playgrounds/openshift45) like us there already exist a few persistent volumes on your cluster. If not, you'll need to create one first using: 12 | 13 | ```bash 14 | kubectl apply -f https://raw.githubusercontent.com/openshift-evangelists/kbe/main/specs/pv/pv.yaml 15 | ``` 16 | 17 | In order to use a PV you need to claim it first, using a persistent volume claim (PVC). The PVC requests a PV with your desired specification (size, speed, etc.) from Kubernetes and binds it then to a pod where you can mount it as a volume. So let's create such a [PVC](https://github.com/openshift-evangelists/kbe/blob/main/specs/pv/pvc.yaml), asking Kubernetes for 1 GB of storage using the default [storage class](https://kubernetes.io/docs/concepts/storage/storage-classes/): 18 | 19 | ```bash 20 | kubectl apply -f https://raw.githubusercontent.com/openshift-evangelists/kbe/main/specs/pv/pvc.yaml 21 | ``` 22 | 23 | ```bash 24 | kubectl get pvc 25 | ``` 26 | ```cat 27 | NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE 28 | myclaim Bound pvc-27fed6b6-3047-11e9-84bb-12b5519f9b58 1Gi RWO gp2-encrypted 18m 29 | ``` 30 | 31 | To understand how the persistency plays out, let's create a [deployment](https://github.com/openshift-evangelists/kbe/blob/main/specs/pv/deploy.yaml) that uses above PVC to mount it as a volume into `/tmp/persistent`: 32 | 33 | ```bash 34 | kubectl apply -f https://raw.githubusercontent.com/openshift-evangelists/kbe/main/specs/pv/deploy.yaml 35 | ``` 36 | 37 | Now we want to test if data in the volume actually persists. For this we find the pod managed by above deployment, exec into its main container and create a file called `data` in the `/tmp/persistent` directory (where we decided to mount the PV): 38 | 39 | ```bash 40 | kubectl get po 41 | ``` 42 | ```cat 43 | NAME READY STATUS RESTARTS AGE 44 | pv-deploy-69959dccb5-jhxx 1/1 Running 0 16m 45 | ``` 46 | 47 | ```bash 48 | kubectl exec -it pv-deploy-69959dccb5-jhxxw -- bash 49 | ``` 50 | ```bash 51 | touch /tmp/persistent/data 52 | ls /tmp/persistent/ 53 | ``` 54 | ```cat 55 | data lost+found 56 | ``` 57 | return 58 | ```bash 59 | exit 60 | ``` 61 | 62 | It's time to destroy the pod and let the deployment launch a new pod. The expectation is that the PV is available again in the new pod and the data in `/tmp/persistent` is still present. Let's check that: 63 | 64 | ```bash 65 | kubectl delete po pv-deploy-69959dccb5-jhxxw 66 | ``` 67 | ```cat 68 | pod pv-deploy-69959dccb5-jhxxw deleted 69 | ``` 70 | 71 | ```bash 72 | kubectl get po 73 | ``` 74 | ```cat 75 | NAME READY STATUS RESTARTS AGE 76 | pv-deploy-69959dccb5-kwrrv 1/1 Running 0 16m 77 | ``` 78 | 79 | ```bash 80 | kubectl exec -it pv-deploy-69959dccb5-kwrrv -- bash 81 | ``` 82 | ```bash 83 | ls /tmp/persistent/ 84 | ``` 85 | ```cat 86 | data lost+found 87 | ``` 88 | ```bash 89 | exit 90 | ``` 91 | 92 | And indeed, the `data` file and its content is still where it is expected to be. 93 | 94 | Note that the default behavior is that even when the deployment is deleted, the PVC (and the PV) continues to exist. This storage protection feature helps avoiding data loss. Once you're sure you don't need the data anymore, you can go ahead and delete the PVC and with it eventually destroy the PV: 95 | 96 | ```bash 97 | kubectl delete pvc myclaim 98 | ``` 99 | ```cat 100 | persistentvolumeclaim "myclaim" deleted 101 | ``` 102 | 103 | The types of PV available in your Kubernetes cluster depend on the environment (on-prem or public cloud). Check out the [Stateful Kubernetes](https://stateful.kubernetes.sh/#storage) reference site if you want to learn more about this topic. 104 | 105 | [Previous](/volumes) | [Next](/secrets) 106 | -------------------------------------------------------------------------------- /content/page/rcs.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = "Replication Controllers" 3 | subtitle = "Kubernetes replication controllers by example" 4 | date = "2019-02-27" 5 | url = "/rcs/" 6 | +++ 7 | 8 | A replication controller (RC) is a supervisor for long-running pods. 9 | An RC will launch a specified number of pods called `replicas` and makes 10 | sure that they keep running, for example when a node fails or something 11 | inside of a pod, that is, in one of its containers goes wrong. 12 | 13 | Let's create an [RC](https://github.com/openshift-evangelists/kbe/blob/main/specs/rcs/rc.yaml) 14 | that supervises a single replica of a pod: 15 | 16 | ```bash 17 | kubectl apply -f https://raw.githubusercontent.com/openshift-evangelists/kbe/main/specs/rcs/rc.yaml 18 | ``` 19 | 20 | You can see the RC and the pod it looks after like so: 21 | 22 | ```bash 23 | kubectl get rc 24 | ``` 25 | ```cat 26 | NAME DESIRED CURRENT READY AGE 27 | rcex 1 1 1 3m 28 | ``` 29 | 30 | ```bash 31 | kubectl get pods --show-labels 32 | ``` 33 | ```cat 34 | NAME READY STATUS RESTARTS AGE LABELS 35 | rcex-qrv8j 1/1 Running 0 4m app=sise 36 | ``` 37 | 38 | Note two things here: 39 | 40 | - the supervised pod got a random name assigned 41 | (`rcex-qrv8j`) 42 | - the way the RC keeps track of its pods is via the label, here `app=sise` 43 | 44 | To scale up, that is, to increase the number of replicas, do: 45 | 46 | ```bash 47 | kubectl scale --replicas=3 rc/rcex 48 | ``` 49 | ```bash 50 | kubectl get pods -l app=sise 51 | ``` 52 | ```cat 53 | NAME READY STATUS RESTARTS AGE 54 | rcex-1rh9r 1/1 Running 0 54s 55 | rcex-lv6xv 1/1 Running 0 54s 56 | rcex-qrv8j 1/1 Running 0 10m 57 | 58 | ``` 59 | 60 | Finally, to get rid of the RC and the pods it is supervising, use: 61 | 62 | ```bash 63 | kubectl delete rc rcex 64 | ``` 65 | ```cat 66 | replicationcontroller "rcex" deleted 67 | ``` 68 | 69 | Note that, going forward, the RCs are called [replica sets](https://kubernetes.io/docs/concepts/workloads/controllers/replicaset/) (RS), supporting set-based selectors. The RS are already in use in the context of [deployments](/deployments/). 70 | 71 | [Previous](/labels) | [Next](/deployments) 72 | -------------------------------------------------------------------------------- /content/page/sd.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = "Service Discovery" 3 | subtitle = "Kubernetes service discovery by example" 4 | date = "2019-02-27" 5 | url = "/sd/" 6 | +++ 7 | 8 | Service discovery is the process of figuring out how to connect to a [service](/services/). 9 | While there is a service discovery option based on [environment variables](https://kubernetes.io/docs/concepts/services-networking/connect-applications-service/#environment-variables) available, 10 | the DNS-based service discovery is preferable. Note that [Kube DNS is a cluster add-on](https://github.com/kubernetes/kubernetes/blob/master/cluster/addons/dns/kube-dns/README.md), which means that it may need to installed, configured, or enabled in order to function correctly. 11 | 12 | Let's create a [service](https://github.com/openshift-evangelists/kbe/blob/main/specs/sd/svc.yaml) named 13 | `thesvc` and an [RC](https://github.com/openshift-evangelists/kbe/blob/main/specs/sd/rc.yaml) supervising 14 | some pods along with it: 15 | 16 | ```bash 17 | kubectl apply -f https://raw.githubusercontent.com/openshift-evangelists/kbe/main/specs/sd/rc.yaml 18 | ``` 19 | 20 | ```bash 21 | kubectl apply -f https://raw.githubusercontent.com/openshift-evangelists/kbe/main/specs/sd/svc.yaml 22 | ``` 23 | 24 | Now we want to connect to the `thesvc` service from within the cluster, say, from another service. 25 | To simulate this, we create a [jump pod](https://github.com/openshift-evangelists/kbe/blob/main/specs/sd/jumpod.yaml) 26 | in the same namespace (`default`, since we didn't specify anything else): 27 | 28 | ```bash 29 | kubectl apply -f https://raw.githubusercontent.com/openshift-evangelists/kbe/main/specs/sd/jumpod.yaml 30 | ``` 31 | 32 | The DNS add-on will make sure that our service `thesvc` is available via the FQDN 33 | `thesvc.default.svc.cluster.local` from other pods in the cluster. Let's try it out: 34 | 35 | ```bash 36 | kubectl exec -it jumpod -c shell -- ping thesvc.default.svc.cluster.local 37 | ``` 38 | ```cat 39 | PING thesvc.default.svc.cluster.local (172.30.251.137) 56(84) bytes of data. 40 | ... 41 | ``` 42 | 43 | Send a break signal (Ctrl-C) to close the connection 44 | ```bash 45 | ^C 46 | ``` 47 | 48 | The answer to the `ping` tells us that the service is available via the cluster 49 | IP `172.30.251.137`. We can directly connect to and consume the service (in the same namespace) like so: 50 | 51 | ```bash 52 | kubectl exec -it jumpod -c shell -- curl http://thesvc/info 53 | ``` 54 | ```cat 55 | {"host": "thesvc", "version": "0.5.0", "from": "172.17.0.5"} 56 | ``` 57 | 58 | Note that the IP address `172.17.0.5` above is the cluster-internal IP address 59 | of the jump pod. 60 | 61 | To access a service that is deployed in a different namespace than the one you're 62 | accessing it from, use a FQDN in the form `$SVC.$NAMESPACE.svc.cluster.local`. 63 | 64 | Let's see how that works by creating: 65 | 66 | 1. a [namespace](https://github.com/openshift-evangelists/kbe/blob/main/specs/sd/other-ns.yaml) `other` 67 | 1. a [service](https://github.com/openshift-evangelists/kbe/blob/main/specs/sd/other-svc.yaml) `thesvc` in namespace `other` 68 | 1. an [RC](https://github.com/openshift-evangelists/kbe/blob/main/specs/sd/other-rc.yaml) supervising the pods, also in namespace `other` 69 | 70 | If you're not familiar with namespaces, check out the [namespace examples](/ns/) first. 71 | 72 | ```bash 73 | kubectl apply -f https://raw.githubusercontent.com/openshift-evangelists/kbe/main/specs/sd/other-ns.yaml 74 | ``` 75 | ```bash 76 | kubectl apply -f https://raw.githubusercontent.com/openshift-evangelists/kbe/main/specs/sd/other-rc.yaml 77 | ``` 78 | ```bash 79 | kubectl apply -f https://raw.githubusercontent.com/openshift-evangelists/kbe/main/specs/sd/other-svc.yaml 80 | ``` 81 | 82 | We're now in the position to consume the service `thesvc` in namespace `other` from the 83 | `default` namespace (again via the jump pod): 84 | 85 | ```bash 86 | kubectl exec -it jumpod -c shell -- curl http://thesvc.other/info 87 | ``` 88 | ```cat 89 | {"host": "thesvc.other", "version": "0.5.0", "from": "172.17.0.5"} 90 | ``` 91 | 92 | Summing up, DNS-based service discovery provides a flexible and generic way to 93 | connect to services across the cluster. 94 | 95 | You can destroy all the resources created with: 96 | 97 | ```bash 98 | kubectl delete pods jumpod 99 | ``` 100 | ```bash 101 | kubectl delete svc thesvc 102 | ``` 103 | ```bash 104 | kubectl delete rc rcsise 105 | ``` 106 | ```bash 107 | kubectl delete ns other 108 | ``` 109 | 110 | Keep in mind that removing a namespace will destroy every resource inside. 111 | 112 | [Previous](/services) | [Next](/pf) 113 | -------------------------------------------------------------------------------- /content/page/secrets.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = "Secrets" 3 | subtitle = "Kubernetes secrets by example" 4 | date = "2019-02-28" 5 | url = "/secrets/" 6 | +++ 7 | 8 | You don't want sensitive information such as a database password or an 9 | API key kept around in clear text. Secrets provide you with a mechanism 10 | to use such information in a safe and reliable way with the following properties: 11 | 12 | - Secrets are namespaced objects, that is, exist in the context of a namespace 13 | - You can access them via a volume or an environment variable from a container running in a pod 14 | - The secret data on nodes is stored in [tmpfs](https://www.kernel.org/doc/Documentation/filesystems/tmpfs.txt) volumes 15 | - A per-secret size limit of 1MB exists 16 | - The API server stores secrets as plaintext in etcd 17 | 18 | Let's create a secret `apikey` that holds a (made-up) API key: 19 | 20 | ```bash 21 | echo -n "A19fh68B001j" > ./apikey.txt 22 | ``` 23 | ```bash 24 | kubectl create secret generic apikey --from-file=./apikey.txt 25 | ``` 26 | ```cat 27 | secret "apikey" created 28 | ``` 29 | ```bash 30 | kubectl describe secrets/apikey 31 | ``` 32 | ```cat 33 | Name: apikey 34 | Namespace: default 35 | Labels: 36 | Annotations: 37 | 38 | Type: Opaque 39 | 40 | Data 41 | ==== 42 | apikey.txt: 12 bytes 43 | ``` 44 | 45 | Now let's use the secret in a [pod](https://github.com/openshift-evangelists/kbe/blob/main/specs/secrets/pod.yaml) 46 | via a [volume](/volumes/): 47 | 48 | 49 | ```bash 50 | kubectl apply -f https://raw.githubusercontent.com/openshift-evangelists/kbe/main/specs/secrets/pod.yaml 51 | ``` 52 | 53 | If we now exec into the container we see the secret mounted at `/tmp/apikey`: 54 | 55 | ```bash 56 | kubectl exec -it consumesec -c shell -- bash 57 | ``` 58 | ```bash 59 | mount | grep apikey 60 | ``` 61 | ```cat 62 | tmpfs on /tmp/apikey type tmpfs (ro,relatime) 63 | ``` 64 | ```bash 65 | cat /tmp/apikey/apikey.txt 66 | ``` 67 | ```cat 68 | A19fh68B001j 69 | ``` 70 | return 71 | ```bash 72 | exit 73 | ``` 74 | 75 | Note that for service accounts Kubernetes automatically creates secrets containing 76 | credentials for accessing the API and modifies your pods to use this type of secret. 77 | 78 | You can remove both the pod and the secret with: 79 | 80 | ```bash 81 | kubectl delete pod/consumesec secret/apikey 82 | ``` 83 | 84 | [Previous](/volumes) | [Next](/logging) 85 | -------------------------------------------------------------------------------- /content/page/services.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = "Services" 3 | subtitle = "Kubernetes services by example" 4 | date = "2019-02-27" 5 | url = "/services/" 6 | +++ 7 | 8 | A service is an abstraction for pods, providing a stable, so called virtual IP (VIP) address. While pods may come and go and with it their IP addresses, a service allows clients to reliably connect to the containers running in the pod using the VIP. The `virtual` in VIP means it is not an actual IP address connected to a network interface, but its purpose is purely to forward traffic to one or more pods. Keeping the mapping between the VIP and the 9 | pods up-to-date is the job of [kube-proxy](https://kubernetes.io/docs/admin/kube-proxy/), a process that runs on every node, which queries the API server to learn about 10 | new services in the cluster. 11 | 12 | Let's create a pod supervised by an [RC](https://github.com/openshift-evangelists/kbe/blob/main/specs/services/rc.yaml) 13 | and a [service](https://github.com/openshift-evangelists/kbe/blob/main/specs/services/svc.yaml) 14 | along with it: 15 | 16 | ```bash 17 | kubectl apply -f https://raw.githubusercontent.com/openshift-evangelists/kbe/main/specs/services/rc.yaml 18 | ``` 19 | ```bash 20 | kubectl apply -f https://raw.githubusercontent.com/openshift-evangelists/kbe/main/specs/services/svc.yaml 21 | ``` 22 | 23 | Now we have the supervised pod running: 24 | 25 | ```bash 26 | kubectl get pods -l app=sise 27 | ``` 28 | ```cat 29 | NAME READY STATUS RESTARTS AGE 30 | rcsise-6nq3k 1/1 Running 0 57s 31 | ``` 32 | 33 | A new pod name should be generated each time this example is run. Make sure to include your own pod name when running the following examples: 34 | 35 | ```bash 36 | kubectl describe pod rcsise-6nq3k 37 | ``` 38 | ```cat 39 | Name: rcsise-6nq3k 40 | Namespace: default 41 | Security Policy: restricted 42 | Node: localhost/192.168.99.100 43 | Start Time: Tue, 25 Apr 2017 14:47:45 +0100 44 | Labels: app=sise 45 | Status: Running 46 | IP: 172.17.0.3 47 | Controllers: ReplicationController/rcsise 48 | Containers: 49 | ... 50 | ``` 51 | 52 | You can, from within the cluster, access the pod directly via its assigned IP `172.17.0.3`: 53 | 54 | ```bash 55 | kubectl exec rcsise-6nq3k -t -- curl -s 172.17.0.3:9876/info 56 | ``` 57 | ```cat 58 | {"host": "172.17.0.3:9876", "version": "0.5.0", "from": "172.17.0.1"} 59 | ``` 60 | 61 | This is however, as mentioned above, not advisable since the IPs assigned 62 | to pods may change as pods are migrated or rescheduled. Hence, enter the included `simpleservice` service endpoint example: 63 | 64 | ```bash 65 | kubectl get svc 66 | ``` 67 | ```cat 68 | NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGE 69 | simpleservice 172.30.228.255 80/TCP 5m 70 | ``` 71 | ```bash 72 | kubectl describe svc simpleservice 73 | ``` 74 | ```cat 75 | Name: simpleservice 76 | Namespace: default 77 | Labels: 78 | Selector: app=sise 79 | Type: ClusterIP 80 | IP: 172.30.228.255 81 | Port: 80/TCP 82 | Endpoints: 172.17.0.3:9876 83 | Session Affinity: None 84 | No events. 85 | ``` 86 | 87 | The `service` resource uses labels to identify which pods it will forward traffic to. In our case, pods labeled with `app=sise` will receive traffic. 88 | 89 | From within the cluster, we can now access any affiliated pods using the IP address of the `simpleservice` svc endpoint on port `80`: 90 | 91 | ```bash 92 | kubectl exec rcsise-6nq3k -t -- curl -s 172.30.228.255:80/info 93 | ``` 94 | ```cat 95 | {"host": "172.30.228.255", "version": "0.5.0", "from": "10.0.2.15"} 96 | ``` 97 | 98 | KubeDNS even provides basic name resolution for kubernetes `services` (within the same kubernetes namespace). This allows us to connect to pods using the associated service name - No need to including IP addresses or port numbers! 99 | 100 | ```bash 101 | kubectl exec rcsise-6nq3k -t -- curl -s simpleservice/info 102 | ``` 103 | ```cat 104 | {"host": "simpleservice", "version": "0.5.0", "from": "10.0.2.15"} 105 | ``` 106 | 107 | What makes the VIP `172.30.228.255` forward the traffic to the pod? 108 | The answer is: [IPtables](https://wiki.centos.org/HowTos/Network/IPTables), 109 | which is essentially a long list of rules that tells the Linux kernel what to do 110 | with a certain IP package. 111 | 112 | Looking at the rules that concern our service (executed on a cluster node) yields: 113 | 114 | ```bash 115 | minikube ssh 116 | ``` 117 | ```bash 118 | sudo iptables-save | grep simpleservice 119 | ``` 120 | ```cat 121 | -A KUBE-SEP-4SQFZS32ZVMTQEZV -s 172.17.0.3/32 -m comment --comment "default/simpleservice:" -j KUBE-MARK-MASQ 122 | -A KUBE-SEP-4SQFZS32ZVMTQEZV -p tcp -m comment --comment "default/simpleservice:" -m tcp -j DNAT --to-destination 172.17.0.3:9876 123 | -A KUBE-SERVICES -d 172.30.228.255/32 -p tcp -m comment --comment "default/simpleservice: cluster IP" -m tcp --dport 80 -j KUBE-SVC-EZC6WLOVQADP4IAW 124 | -A KUBE-SVC-EZC6WLOVQADP4IAW -m comment --comment "default/simpleservice:" -j KUBE-SEP-4SQFZS32ZVMTQEZV 125 | ``` 126 | 127 | exit the pod to continue 128 | ```bash 129 | exit 130 | ``` 131 | 132 | Above you can see the four rules that `kube-proxy` has thankfully added to the 133 | routing table, essentially stating that TCP traffic to `172.30.228.255:80` 134 | should be forwarded to `172.17.0.3:9876`, which is our pod. 135 | 136 | Let’s now add a second pod by scaling up the RC supervising it: 137 | 138 | ```bash 139 | kubectl scale --replicas=2 rc/rcsise 140 | ``` 141 | ```cat 142 | replicationcontroller "rcsise" scaled 143 | ``` 144 | 145 | ```bash 146 | kubectl get pods -l app=sise 147 | ``` 148 | ```cat 149 | NAME READY STATUS RESTARTS AGE 150 | rcsise-6nq3k 1/1 Running 0 15m 151 | rcsise-nv8zm 1/1 Running 0 5s 152 | ``` 153 | 154 | When we now check the relevant parts of the routing table again we notice 155 | the addition of a bunch of IPtables rules: 156 | 157 | ```bash 158 | minikube ssh 159 | ``` 160 | ```bash 161 | sudo iptables-save | grep simpleservice 162 | ``` 163 | ```cat 164 | -A KUBE-SEP-4SQFZS32ZVMTQEZV -s 172.17.0.3/32 -m comment --comment "default/simpleservice:" -j KUBE-MARK-MASQ 165 | -A KUBE-SEP-4SQFZS32ZVMTQEZV -p tcp -m comment --comment "default/simpleservice:" -m tcp -j DNAT --to-destination 172.17.0.3:9876 166 | -A KUBE-SEP-PXYYII6AHMUWKLYX -s 172.17.0.4/32 -m comment --comment "default/simpleservice:" -j KUBE-MARK-MASQ 167 | -A KUBE-SEP-PXYYII6AHMUWKLYX -p tcp -m comment --comment "default/simpleservice:" -m tcp -j DNAT --to-destination 172.17.0.4:9876 168 | -A KUBE-SERVICES -d 172.30.228.255/32 -p tcp -m comment --comment "default/simpleservice: cluster IP" -m tcp --dport 80 -j KUBE-SVC-EZC6WLOVQADP4IAW 169 | -A KUBE-SVC-EZC6WLOVQADP4IAW -m comment --comment "default/simpleservice:" -m statistic --mode random --probability 0.50000000000 -j KUBE-SEP-4SQFZS32ZVMTQEZV 170 | -A KUBE-SVC-EZC6WLOVQADP4IAW -m comment --comment "default/simpleservice:" -j KUBE-SEP-PXYYII6AHMUWKLYX 171 | ``` 172 | 173 | In above routing table listing we see rules for the newly created pod serving at 174 | `172.17.0.4:9876` as well as an additional rule: 175 | 176 | ```cat 177 | -A KUBE-SVC-EZC6WLOVQADP4IAW -m comment --comment "default/simpleservice:" -m statistic --mode random --probability 0.50000000000 -j KUBE-SEP-4SQFZS32ZVMTQEZV 178 | ``` 179 | 180 | return to continue 181 | ```bash 182 | exit 183 | ``` 184 | 185 | This causes the traffic to the service being equally split between our two pods 186 | by invoking the `statistics` module of IPtables. 187 | 188 | You can remove all the resources created by doing: 189 | 190 | ```bash 191 | kubectl delete svc simpleservice 192 | ``` 193 | 194 | ```bash 195 | kubectl delete rc rcsise 196 | ``` 197 | 198 | [Previous](/deployments) | [Next](/sd) 199 | -------------------------------------------------------------------------------- /content/page/statefulset.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = "StatefulSet" 3 | subtitle = "Kubernetes statefulset by example" 4 | date = "2020-03-23" 5 | url = "/statefulset/" 6 | +++ 7 | 8 | If you have a stateless app you want to use a deployment. However, for a stateful app you might want to use a [StatefulSet](https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/). Unlike a deployment, the `StatefulSet` provides certain guarantees about the identity of the pods it is managing (that is, predictable names) and about the startup order. Two more things that are different compared to a deployment: for network communication you need to create a [headless services](https://kubernetes.io/docs/concepts/services-networking/service/#headless-services) and for persistency the `StatefulSet` manages a [persistent volume](/pv) per pod. 9 | 10 | In order to see how this all plays together, we will be using an [educational Kubernetes-native NoSQL datastore](https://blog.openshift.com/kubernetes-statefulset-in-action/). 11 | 12 | Let's start with creating the [stateful app](https://raw.githubusercontent.com/openshift-evangelists/mehdb/main/app.yaml), that is, the `StatefulSet` along with the persistent volumes and the headless service: 13 | 14 | ```bash 15 | kubectl apply -f https://raw.githubusercontent.com/openshift-evangelists/mehdb/main/app.yaml 16 | ``` 17 | 18 | After a minute or so, you can have a look at all the resources that have been created: 19 | 20 | ```bash 21 | kubectl get sts,po,pvc,svc 22 | ``` 23 | ```cat 24 | NAME DESIRED CURRENT AGE 25 | statefulset.apps/mehdb 2 2 1m 26 | 27 | NAME READY STATUS RESTARTS AGE 28 | pod/mehdb-0 1/1 Running 0 1m 29 | pod/mehdb-1 1/1 Running 0 56s 30 | 31 | NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE 32 | persistentvolumeclaim/data-mehdb-0 Bound pvc-bc2d9b3b-310d-11e9-aeff-123713f594ec 1Gi RWO ebs 1m 33 | persistentvolumeclaim/data-mehdb-1 Bound pvc-d4b7620f-310d-11e9-aeff-123713f594ec 1Gi RWO ebs 56s 34 | 35 | NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE 36 | service/mehdb ClusterIP None 9876/TCP 1m 37 | ``` 38 | 39 | Now we can check if the stateful app is working properly. To do this, we use the `/status` endpoint of the headless service `mehdb:9876` and since we haven't put any data yet into the datastore, we'd expect that `0` keys are reported: 40 | 41 | ```bash 42 | kubectl run -it --rm jumpod --restart=Never --image=quay.io/openshiftlabs/jump:0.2 -- curl -s mehdb:9876/status?level=full 43 | ``` 44 | ```cat 45 | 0 46 | pod "jumpod" deleted 47 | ``` 48 | 49 | And indeed we see `0` keys being available, reported above. 50 | 51 | Note that sometimes a `StatefulSet` is not the best fit for your stateful app. You might be better off defining a [custom resource](https://kubernetes.io/docs/concepts/extend-kubernetes/api-extension/custom-resources/) along with writing a custom controller to have finer-grained control over your workload. 52 | 53 | We can now cleanup after we are done using: 54 | 55 | ```bash 56 | kubectl delete -f https://raw.githubusercontent.com/openshift-evangelists/mehdb/main/app.yaml 57 | kubectl delete pvc/data-mehdb-0 58 | ``` 59 | 60 | [Previous](/jobs) | [Next](/ic) 61 | -------------------------------------------------------------------------------- /content/page/volumes.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = "Volumes" 3 | subtitle = "Kubernetes volumes by example" 4 | date = "2020-03-19" 5 | url = "/volumes/" 6 | +++ 7 | 8 | A Kubernetes volume is essentially a directory accessible to all containers 9 | running in a pod. In contrast to the container-local filesystem, the data in volumes is preserved across container restarts. The medium backing a volume and its contents are determined by the volume type: 10 | 11 | - node-local types such as `emptyDir` or `hostPath` 12 | - file-sharing types such as `nfs` 13 | - cloud provider-specific types like `awsElasticBlockStore`, `azureDisk`, or `gcePersistentDisk` 14 | - distributed file system types, for example `glusterfs` or `cephfs` 15 | - special-purpose types like `secret`, `gitRepo` 16 | 17 | A special type of volume is `PersistentVolume`, which we will cover elsewhere. 18 | 19 | Let's create a [pod](https://github.com/openshift-evangelists/kbe/blob/main/specs/volumes/pod.yaml) 20 | with two containers that use an `emptyDir` volume to exchange data: 21 | 22 | ```bash 23 | kubectl apply -f https://raw.githubusercontent.com/openshift-evangelists/kbe/main/specs/volumes/pod.yaml 24 | ``` 25 | ```bash 26 | kubectl describe pod sharevol 27 | ``` 28 | ```cat 29 | Name: sharevol 30 | Namespace: default 31 | ... 32 | Volumes: 33 | xchange: 34 | Type: EmptyDir (a temporary directory that shares a pod's lifetime) 35 | Medium: 36 | ``` 37 | 38 | We first exec into one of the containers in the pod, `c1`, check the volume mount 39 | and generate some data: 40 | 41 | ```bash 42 | kubectl exec -it sharevol -c c1 -- bash 43 | ``` 44 | ```bash 45 | mount | grep xchange 46 | ``` 47 | ```cat 48 | /dev/vda3 on /tmp/xchange type xfs (rw,relatime,seclabel,attr2,inode64,rjquota) 49 | ``` 50 | ```bash 51 | echo 'some data' > /tmp/xchange/data 52 | ``` 53 | 54 | exit the pod to continue 55 | ```bash 56 | exit 57 | ``` 58 | 59 | When we now exec into `c2`, the second container running in the pod, we can see 60 | the volume mounted at `/tmp/data` and are able to read the data created in the 61 | previous step: 62 | 63 | ```bash 64 | kubectl exec -it sharevol -c c2 -- bash 65 | ``` 66 | ```bash 67 | mount | grep /tmp/data 68 | ``` 69 | ```cat 70 | /dev/vda3 on /tmp/data type xfs (rw,relatime,seclabel,attr2,inode64,prjquota) 71 | ``` 72 | 73 | ```bash 74 | cat /tmp/data/data/ 75 | ``` 76 | ```cat 77 | cat: /tmp/data/data/: Not a directory 78 | ``` 79 | ```bash 80 | cat /tmp/data/data 81 | ``` 82 | ```cat 83 | some data 84 | ``` 85 | 86 | Note that in each container you need to decide where to mount the volume and 87 | that for `emptyDir` you currently can not specify resource consumption limits. 88 | 89 | return to continue 90 | ```bash 91 | exit 92 | ``` 93 | 94 | You can remove the pod with: 95 | 96 | ```bash 97 | kubectl delete pod/sharevol 98 | ``` 99 | 100 | As already described, this will destroy the shared volume and all its contents. 101 | 102 | [Previous](/ns) | [Next](/pv) 103 | -------------------------------------------------------------------------------- /specs/deployments/d09.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: sise-deploy 5 | spec: 6 | replicas: 2 7 | selector: 8 | matchLabels: 9 | app: sise 10 | template: 11 | metadata: 12 | labels: 13 | app: sise 14 | spec: 15 | containers: 16 | - name: sise 17 | image: quay.io/openshiftlabs/simpleservice:0.5.0 18 | ports: 19 | - containerPort: 9876 20 | env: 21 | - name: SIMPLE_SERVICE_VERSION 22 | value: "0.9" 23 | -------------------------------------------------------------------------------- /specs/deployments/d10.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: sise-deploy 5 | spec: 6 | replicas: 2 7 | selector: 8 | matchLabels: 9 | app: sise 10 | template: 11 | metadata: 12 | labels: 13 | app: sise 14 | spec: 15 | containers: 16 | - name: sise 17 | image: quay.io/openshiftlabs/simpleservice:0.5.0 18 | ports: 19 | - containerPort: 9876 20 | env: 21 | - name: SIMPLE_SERVICE_VERSION 22 | value: "1.0" 23 | -------------------------------------------------------------------------------- /specs/envs/pod.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Pod 3 | metadata: 4 | name: envs 5 | spec: 6 | containers: 7 | - name: sise 8 | image: quay.io/openshiftlabs/simpleservice:0.5.0 9 | ports: 10 | - containerPort: 9876 11 | env: 12 | - name: SIMPLE_SERVICE_VERSION 13 | value: "1.0" 14 | -------------------------------------------------------------------------------- /specs/healthz/badpod.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Pod 3 | metadata: 4 | name: badpod 5 | spec: 6 | containers: 7 | - name: sise 8 | image: quay.io/openshiftlabs/simpleservice:0.5.0 9 | ports: 10 | - containerPort: 9876 11 | env: 12 | - name: HEALTH_MIN 13 | value: "1000" 14 | - name: HEALTH_MAX 15 | value: "4000" 16 | livenessProbe: 17 | initialDelaySeconds: 2 18 | periodSeconds: 5 19 | httpGet: 20 | path: /health 21 | port: 9876 22 | -------------------------------------------------------------------------------- /specs/healthz/nap-rc.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ReplicationController 3 | metadata: 4 | name: rcsise 5 | spec: 6 | replicas: 5 7 | selector: 8 | app: sise 9 | template: 10 | metadata: 11 | name: somebadpods 12 | labels: 13 | app: sise 14 | spec: 15 | containers: 16 | - name: sise 17 | image: quay.io/openshiftlabs/simpleservice:0.5.0 18 | ports: 19 | - containerPort: 9876 20 | env: 21 | - name: HEALTH_MAX 22 | value: "3100" 23 | readinessProbe: 24 | initialDelaySeconds: 3 25 | httpGet: 26 | path: /health 27 | port: 9876 28 | -------------------------------------------------------------------------------- /specs/healthz/nap-svc.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: notallpodsserving 5 | spec: 6 | ports: 7 | - port: 80 8 | targetPort: 9876 9 | selector: 10 | app: sise 11 | -------------------------------------------------------------------------------- /specs/healthz/pod.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Pod 3 | metadata: 4 | name: hc 5 | spec: 6 | containers: 7 | - name: sise 8 | image: quay.io/openshiftlabs/simpleservice:0.5.0 9 | ports: 10 | - containerPort: 9876 11 | livenessProbe: 12 | initialDelaySeconds: 2 13 | periodSeconds: 5 14 | httpGet: 15 | path: /health 16 | port: 9876 17 | -------------------------------------------------------------------------------- /specs/healthz/ready.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Pod 3 | metadata: 4 | name: ready 5 | spec: 6 | containers: 7 | - name: sise 8 | image: quay.io/openshiftlabs/simpleservice:0.5.0 9 | ports: 10 | - containerPort: 9876 11 | readinessProbe: 12 | initialDelaySeconds: 10 13 | httpGet: 14 | path: /health 15 | port: 9876 16 | -------------------------------------------------------------------------------- /specs/ic/deploy.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: ic-deploy 5 | spec: 6 | replicas: 1 7 | selector: 8 | matchLabels: 9 | app: ic 10 | template: 11 | metadata: 12 | labels: 13 | app: ic 14 | spec: 15 | initContainers: 16 | - name: msginit 17 | image: centos:7 18 | command: 19 | - "bin/bash" 20 | - "-c" 21 | - "echo INIT_DONE > /ic/this" 22 | volumeMounts: 23 | - mountPath: /ic 24 | name: msg 25 | containers: 26 | - name: main 27 | image: centos:7 28 | command: 29 | - "bin/bash" 30 | - "-c" 31 | - "while true; do cat /ic/this; sleep 5; done" 32 | volumeMounts: 33 | - mountPath: /ic 34 | name: msg 35 | volumes: 36 | - name: msg 37 | emptyDir: {} 38 | -------------------------------------------------------------------------------- /specs/jobs/job.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: batch/v1 2 | kind: Job 3 | metadata: 4 | name: countdown 5 | spec: 6 | template: 7 | metadata: 8 | name: countdown 9 | spec: 10 | containers: 11 | - name: counter 12 | image: centos:7 13 | command: 14 | - "bin/bash" 15 | - "-c" 16 | - "for i in 9 8 7 6 5 4 3 2 1 ; do echo $i ; done" 17 | restartPolicy: Never 18 | -------------------------------------------------------------------------------- /specs/labels/anotherpod.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Pod 3 | metadata: 4 | name: labelexother 5 | labels: 6 | env: production 7 | owner: michael 8 | spec: 9 | containers: 10 | - name: sise 11 | image: quay.io/openshiftlabs/simpleservice:0.5.0 12 | ports: 13 | - containerPort: 9876 14 | -------------------------------------------------------------------------------- /specs/labels/pod.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Pod 3 | metadata: 4 | name: labelex 5 | labels: 6 | env: development 7 | spec: 8 | containers: 9 | - name: sise 10 | image: quay.io/openshiftlabs/simpleservice:0.5.0 11 | ports: 12 | - containerPort: 9876 13 | -------------------------------------------------------------------------------- /specs/logging/oneshotpod.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Pod 3 | metadata: 4 | name: oneshot 5 | spec: 6 | containers: 7 | - name: gen 8 | image: centos:7 9 | command: 10 | - "bin/bash" 11 | - "-c" 12 | - "for i in 9 8 7 6 5 4 3 2 1 ; do echo $i ; done" 13 | -------------------------------------------------------------------------------- /specs/logging/pod.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Pod 3 | metadata: 4 | name: logme 5 | spec: 6 | containers: 7 | - name: gen 8 | image: centos:7 9 | command: 10 | - "bin/bash" 11 | - "-c" 12 | - "while true; do echo $(date) | tee /dev/stderr; sleep 1; done" 13 | -------------------------------------------------------------------------------- /specs/nodes/pod.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Pod 3 | metadata: 4 | name: onspecificnode 5 | spec: 6 | containers: 7 | - name: sise 8 | image: quay.io/openshiftlabs/simpleservice:0.5.0 9 | ports: 10 | - containerPort: 9876 11 | nodeSelector: 12 | shouldrun: here 13 | -------------------------------------------------------------------------------- /specs/ns/ns.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Namespace 3 | metadata: 4 | name: test 5 | -------------------------------------------------------------------------------- /specs/ns/pod.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Pod 3 | metadata: 4 | name: podintest 5 | spec: 6 | containers: 7 | - name: sise 8 | image: quay.io/openshiftlabs/simpleservice:0.5.0 9 | ports: 10 | - containerPort: 9876 11 | -------------------------------------------------------------------------------- /specs/pf/app.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: sise-deploy 5 | spec: 6 | replicas: 1 7 | selector: 8 | matchLabels: 9 | app: sise 10 | template: 11 | metadata: 12 | labels: 13 | app: sise 14 | spec: 15 | containers: 16 | - name: sise 17 | image: quay.io/openshiftlabs/simpleservice:0.5.0 18 | ports: 19 | - containerPort: 9876 20 | --- 21 | apiVersion: v1 22 | kind: Service 23 | metadata: 24 | name: simpleservice 25 | spec: 26 | ports: 27 | - port: 80 28 | targetPort: 9876 29 | selector: 30 | app: sise 31 | -------------------------------------------------------------------------------- /specs/pods/constraint-pod.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Pod 3 | metadata: 4 | name: constraintpod 5 | spec: 6 | containers: 7 | - name: sise 8 | image: quay.io/openshiftlabs/simpleservice:0.5.0 9 | ports: 10 | - containerPort: 9876 11 | resources: 12 | limits: 13 | memory: "64Mi" 14 | cpu: "500m" 15 | -------------------------------------------------------------------------------- /specs/pods/oc-constraint-pod.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Pod 3 | metadata: 4 | name: constraintpod 5 | spec: 6 | containers: 7 | - name: sise 8 | image: quay.io/openshiftlabs/simpleservice:0.5.0 9 | ports: 10 | - containerPort: 9876 11 | resources: 12 | limits: 13 | cpu: "30m" 14 | memory: "250Mi" 15 | -------------------------------------------------------------------------------- /specs/pods/pod.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Pod 3 | metadata: 4 | name: twocontainers 5 | spec: 6 | containers: 7 | - name: sise 8 | image: quay.io/openshiftlabs/simpleservice:0.5.0 9 | ports: 10 | - containerPort: 9876 11 | - name: shell 12 | image: centos:7 13 | command: 14 | - "bin/bash" 15 | - "-c" 16 | - "sleep 10000" 17 | -------------------------------------------------------------------------------- /specs/pv/deploy.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: pv-deploy 5 | spec: 6 | replicas: 1 7 | selector: 8 | matchLabels: 9 | app: mypv 10 | template: 11 | metadata: 12 | labels: 13 | app: mypv 14 | spec: 15 | containers: 16 | - name: shell 17 | image: centos:7 18 | command: 19 | - "bin/bash" 20 | - "-c" 21 | - "sleep 10000" 22 | volumeMounts: 23 | - name: mypd 24 | mountPath: "/tmp/persistent" 25 | volumes: 26 | - name: mypd 27 | persistentVolumeClaim: 28 | claimName: myclaim 29 | -------------------------------------------------------------------------------- /specs/pv/pv.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: PersistentVolume 4 | metadata: 5 | annotations: 6 | pv.kubernetes.io/bound-by-controller: "yes" 7 | finalizers: 8 | - kubernetes.io/pv-protection 9 | labels: 10 | volume: pv0001 11 | name: pv0001 12 | resourceVersion: "227035" 13 | selfLink: /api/v1/persistentvolumes/pv0001 14 | spec: 15 | accessModes: 16 | - ReadWriteOnce 17 | capacity: 18 | storage: 5Gi 19 | claimRef: 20 | apiVersion: v1 21 | kind: PersistentVolumeClaim 22 | name: myclaim 23 | namespace: default 24 | resourceVersion: "227033" 25 | hostPath: 26 | path: /mnt/pv-data/pv0001 27 | type: "" 28 | persistentVolumeReclaimPolicy: Recycle 29 | volumeMode: Filesystem 30 | status: 31 | phase: Bound 32 | 33 | -------------------------------------------------------------------------------- /specs/pv/pvc.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: PersistentVolumeClaim 3 | metadata: 4 | name: myclaim 5 | spec: 6 | accessModes: 7 | - ReadWriteOnce 8 | resources: 9 | requests: 10 | storage: 1Gi -------------------------------------------------------------------------------- /specs/rcs/rc.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ReplicationController 3 | metadata: 4 | name: rcex 5 | spec: 6 | replicas: 1 7 | selector: 8 | app: sise 9 | template: 10 | metadata: 11 | name: somename 12 | labels: 13 | app: sise 14 | spec: 15 | containers: 16 | - name: sise 17 | image: quay.io/openshiftlabs/simpleservice:0.5.0 18 | ports: 19 | - containerPort: 9876 20 | -------------------------------------------------------------------------------- /specs/sd/jumpod.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Pod 3 | metadata: 4 | name: jumpod 5 | spec: 6 | containers: 7 | - name: shell 8 | image: centos:7 9 | command: 10 | - "bin/bash" 11 | - "-c" 12 | - "sleep 10000" 13 | -------------------------------------------------------------------------------- /specs/sd/other-ns.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Namespace 3 | metadata: 4 | name: other 5 | -------------------------------------------------------------------------------- /specs/sd/other-rc.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ReplicationController 3 | metadata: 4 | name: rcsise 5 | namespace: other 6 | spec: 7 | replicas: 2 8 | selector: 9 | app: sise 10 | template: 11 | metadata: 12 | name: somename 13 | labels: 14 | app: sise 15 | spec: 16 | containers: 17 | - name: sise 18 | image: quay.io/openshiftlabs/simpleservice:0.5.0 19 | ports: 20 | - containerPort: 9876 21 | -------------------------------------------------------------------------------- /specs/sd/other-svc.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: thesvc 5 | namespace: other 6 | spec: 7 | ports: 8 | - port: 80 9 | targetPort: 9876 10 | selector: 11 | app: sise 12 | -------------------------------------------------------------------------------- /specs/sd/rc.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ReplicationController 3 | metadata: 4 | name: rcsise 5 | spec: 6 | replicas: 2 7 | selector: 8 | app: sise 9 | template: 10 | metadata: 11 | name: somename 12 | labels: 13 | app: sise 14 | spec: 15 | containers: 16 | - name: sise 17 | image: quay.io/openshiftlabs/simpleservice:0.5.0 18 | ports: 19 | - containerPort: 9876 20 | -------------------------------------------------------------------------------- /specs/sd/svc.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: thesvc 5 | spec: 6 | ports: 7 | - port: 80 8 | targetPort: 9876 9 | selector: 10 | app: sise 11 | -------------------------------------------------------------------------------- /specs/secrets/pod.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Pod 3 | metadata: 4 | name: consumesec 5 | spec: 6 | containers: 7 | - name: shell 8 | image: centos:7 9 | command: 10 | - "bin/bash" 11 | - "-c" 12 | - "sleep 10000" 13 | volumeMounts: 14 | - name: apikeyvol 15 | mountPath: "/tmp/apikey" 16 | readOnly: true 17 | volumes: 18 | - name: apikeyvol 19 | secret: 20 | secretName: apikey 21 | -------------------------------------------------------------------------------- /specs/services/rc.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ReplicationController 3 | metadata: 4 | name: rcsise 5 | spec: 6 | replicas: 1 7 | selector: 8 | app: sise 9 | template: 10 | metadata: 11 | name: somename 12 | labels: 13 | app: sise 14 | spec: 15 | containers: 16 | - name: sise 17 | image: quay.io/openshiftlabs/simpleservice:0.5.0 18 | ports: 19 | - containerPort: 9876 20 | -------------------------------------------------------------------------------- /specs/services/svc.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: simpleservice 5 | spec: 6 | ports: 7 | - port: 80 8 | targetPort: 9876 9 | selector: 10 | app: sise 11 | -------------------------------------------------------------------------------- /specs/volumes/pod.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Pod 3 | metadata: 4 | name: sharevol 5 | spec: 6 | containers: 7 | - name: c1 8 | image: centos:7 9 | command: 10 | - "bin/bash" 11 | - "-c" 12 | - "sleep 10000" 13 | volumeMounts: 14 | - name: xchange 15 | mountPath: "/tmp/xchange" 16 | - name: c2 17 | image: centos:7 18 | command: 19 | - "bin/bash" 20 | - "-c" 21 | - "sleep 10000" 22 | volumeMounts: 23 | - name: xchange 24 | mountPath: "/tmp/data" 25 | volumes: 26 | - name: xchange 27 | emptyDir: {} 28 | -------------------------------------------------------------------------------- /static/img/Icon-Red_Hat-Media_and_documents-Paper_Stack_Blank-A-Black-RGB.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openshift-evangelists/kbe/445a629a29bdca5280bb94c2f455129606e08c19/static/img/Icon-Red_Hat-Media_and_documents-Paper_Stack_Blank-A-Black-RGB.png -------------------------------------------------------------------------------- /static/img/kbe-logo.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openshift-evangelists/kbe/445a629a29bdca5280bb94c2f455129606e08c19/static/img/kbe-logo.ico -------------------------------------------------------------------------------- /static/img/kbe-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openshift-evangelists/kbe/445a629a29bdca5280bb94c2f455129606e08c19/static/img/kbe-logo.png -------------------------------------------------------------------------------- /themes/beautifulhugo/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Original work Copyright (c) 2015 Dean Attali 4 | Modified work Copyright (c) 2017 Michael Romero 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | -------------------------------------------------------------------------------- /themes/beautifulhugo/README.md: -------------------------------------------------------------------------------- 1 | # Beautiful Hugo - A port of Beautiful Jekyll Theme 2 | 3 | ![Beautiful Hugo Theme Screenshot](https://github.com/halogenica/beautifulhugo/blob/master/images/screenshot.png) 4 | 5 | ## Installation 6 | 7 | $ mkdir themes 8 | $ cd themes 9 | $ git clone https://github.com/halogenica/beautifulhugo.git beautifulhugo 10 | 11 | See [the Hugo documentation](http://gohugo.io/themes/installing/) for more information. 12 | 13 | ## Extra Features 14 | 15 | ### Responsive 16 | 17 | This theme is designed to look great on both large-screen and small-screen (mobile) devices. 18 | 19 | ### Syntax highlighting 20 | 21 | This theme has support for both server side and client side highlighting. 22 | 23 | #### Server side syntax highlighting 24 | 25 | Use the `highlight` shortcode (with Pygments), 26 | see [the Hugo documentation](http://gohugo.io/extras/highlighting/) for more information. 27 | 28 | To use this feature install Pygments (`pip install Pygments`) and add `pygmentsuseclasses = true` to your `config.toml`. 29 | 30 | #### Client side syntax highlighting 31 | 32 | Use triple backticks ( ``` ) or triple tilde ( ~~~ ) around code blocks. 33 | 34 | Client side highlighting does not require pygments to be installed. 35 | 36 | ### Disqus support 37 | 38 | To use this feature, uncomment and fill out the `disqusShortname` parameter in `config.toml`. 39 | 40 | ### Google Analytics 41 | 42 | To add Google Analytics, simply sign up to [Google Analytics](http://www.google.com/analytics/) to obtain your Google Tracking ID, and add this tracking ID to the `googleAnalytics` parameter in `config.toml`. 43 | 44 | ### Commit SHA on the footer 45 | 46 | If the source of your site is in a Git repo, the SHA corresponding to the commit the site is built from can be shown on the footer. To do so, two environment variables have to be set (`GIT_COMMIT_SHA` and `GIT_COMMIT_SHA_SHORT`) and parameter `commit` has to be defined in the config file: 47 | 48 | ``` 49 | [Params] 50 | commit = "https://github.com///tree/" 51 | ``` 52 | 53 | This can be achieved by running the next command prior to calling Hugo: 54 | 55 | ``` 56 | GIT_COMMIT_SHA=`git rev-parse --verify HEAD` GIT_COMMIT_SHA_SHORT=`git rev-parse --short HEAD` 57 | ``` 58 | 59 | See at [xor-gate/xor-gate.org](https://github.com/xor-gate/xor-gate.org) an example of how to add it to a continuous integration system. 60 | 61 | ## About 62 | 63 | This is a port of the Jekyll theme [Beautiful Jekyll](http://deanattali.com/beautiful-jekyll/) by [Dean Attali](http://deanattali.com/aboutme#contact). It supports most of the features of the original theme. 64 | 65 | ## License 66 | 67 | MIT Licensed, see [LICENSE](https://github.com/halogenica/Hugo-BeautifulHugo/blob/master/LICENSE). 68 | -------------------------------------------------------------------------------- /themes/beautifulhugo/archetypes/default.md: -------------------------------------------------------------------------------- 1 | --- 2 | subtitle: "" 3 | tags: [] 4 | --- 5 | -------------------------------------------------------------------------------- /themes/beautifulhugo/data/beautifulhugo/social.toml: -------------------------------------------------------------------------------- 1 | [[social_icons]] 2 | id = "email" 3 | url = "mailto:%s" 4 | title = "Email me" 5 | icon = "fa-envelope" 6 | 7 | [[social_icons]] 8 | id = "facebook" 9 | url = "https://www.facebook.com/%s" 10 | title = "Facebook" 11 | icon = "fa-facebook" 12 | 13 | [[social_icons]] 14 | id = "googleplus" 15 | url = "https://www.plus.google.com/%s" 16 | title = "Google+" 17 | icon = "fa-google-plus" 18 | 19 | [[social_icons]] 20 | id = "github" 21 | url = "https://github.com/%s" 22 | title = "GitHub" 23 | icon = "fa-github" 24 | 25 | [[social_icons]] 26 | id = "twitter" 27 | url = "https://twitter.com/%s" 28 | title = "Twitter" 29 | icon = "fa-twitter" 30 | 31 | [[social_icons]] 32 | id = "reddit" 33 | url = "https://reddit.com/u/%s" 34 | title = "Reddit" 35 | icon = "fa-reddit-alien" 36 | 37 | [[social_icons]] 38 | id = "linkedin" 39 | url = "https://linkedin.com/in/%s" 40 | title = "LinkedIn" 41 | icon = "fa-linkedin" 42 | 43 | [[social_icons]] 44 | id = "xing" 45 | url = "https://www.xing.com/profile/%s" 46 | title = "Xing" 47 | icon = "fa-xing" 48 | 49 | [[social_icons]] 50 | id = "stackoverflow" 51 | url = "https://stackoverflow.com/%s" 52 | title = "StackOverflow" 53 | icon = "fa-stack-overflow" 54 | 55 | [[social_icons]] 56 | id = "snapchat" 57 | url = "https://www.snapchat.com/add/%s" 58 | title = "Snapchat" 59 | icon = "fa-snapchat-ghost" 60 | 61 | [[social_icons]] 62 | id = "instagram" 63 | url = "https://www.instagram.com/%s" 64 | title = "Instagram" 65 | icon = "fa-instagram" 66 | 67 | [[social_icons]] 68 | id = "youtube" 69 | url = "https://www.youtube.com/%s" 70 | title = "Youtube" 71 | icon = "fa-youtube" 72 | 73 | [[social_icons]] 74 | id = "soundcloud" 75 | url = "https://soundcloud.com/%s" 76 | title = "SoundCloud" 77 | icon = "fa-soundcloud" 78 | 79 | [[social_icons]] 80 | id = "spotify" 81 | url = "https://open.spotify.com/user/%s" 82 | title = "Spotify" 83 | icon = "fa-spotify" 84 | 85 | [[social_icons]] 86 | id = "bandcamp" 87 | url = "https://%s.bandcamp.com/" 88 | title = "Bandcamp" 89 | icon = "fa-bandcamp" 90 | 91 | [[social_icons]] 92 | id = "itchio" 93 | url = "https://itch.io/profile/%s" 94 | title = "Itch.io" 95 | icon = "fa-gamepad" 96 | 97 | -------------------------------------------------------------------------------- /themes/beautifulhugo/i18n/en.yaml: -------------------------------------------------------------------------------- 1 | # Content 2 | - id: dateFormat 3 | translation: "January 2, 2006" 4 | - id: postedOnDate 5 | translation: "Posted on {{ .Count }}" 6 | - id: translationsLabel 7 | translation: "Other languages: " 8 | - id: translationsSeparator 9 | translation: ", " 10 | - id: readMore 11 | translation: "Read More" 12 | - id: olderPosts 13 | translation: "Older Posts" 14 | - id: newerPosts 15 | translation: "Newer Posts" 16 | - id: previousPost 17 | translation: "Previous Post" 18 | - id: nextPost 19 | translation: "Next Post" 20 | 21 | # 404 page 22 | - id: pageNotFound 23 | translation: "Whoops, this page doesn't exist. Move along. (404 error)" 24 | 25 | # Footer 26 | - id: poweredBy # Accepts HTML 27 | translation: 'Hugo v{{ .Hugo.Version }} powered  •  Theme by Beautiful Jekyll adapted to Beautiful Hugo' 28 | 29 | # Navigation 30 | - id: toggleNavigation 31 | translation: "Toggle navigation" 32 | - id: languageSwitcherLabel 33 | translation: "Language" 34 | - id: gcseLabelShort 35 | translation: "Search" 36 | - id: gcseLabelLong 37 | translation: "Search {{ .Site.Title }}" 38 | - id: gcseClose 39 | translation: "Close" 40 | 41 | -------------------------------------------------------------------------------- /themes/beautifulhugo/i18n/ja.yaml: -------------------------------------------------------------------------------- 1 | # Content 2 | - id: dateFormat 3 | translation: "2006年1月2日" 4 | - id: postedOnDate 5 | translation: "{{ .Count }}に投稿" 6 | - id: translationsLabel 7 | translation: "翻訳:" 8 | - id: translationsSeparator 9 | translation: "・" 10 | - id: readMore 11 | translation: "続きを読む" 12 | - id: olderPosts 13 | translation: "古いページ" 14 | - id: newerPosts 15 | translation: "新しいページ" 16 | - id: previousPost 17 | translation: "前ページ" 18 | - id: nextPost 19 | translation: "次ページ" 20 | 21 | # 404 page 22 | - id: pageNotFound 23 | translation: "おっと、このページが存在しない。他にあたってください。(404エラー)" 24 | 25 | # Footer 26 | - id: poweredBy # Accepts HTML 27 | translation: '起動力にHugo v{{ .Hugo.Version }}  •  テーマにBeautiful Jekyllに基づいているBeautiful Hugo' 28 | 29 | # Navigation 30 | - id: toggleNavigation 31 | translation: "メニューを切り替え" 32 | - id: gcseLabelShort 33 | translation: "検索" 34 | - id: gcseLabelLong 35 | translation: "{{ .Site.Title }}を検索" 36 | - id: gcseClose 37 | translation: "閉じる" 38 | 39 | -------------------------------------------------------------------------------- /themes/beautifulhugo/layouts/404.html: -------------------------------------------------------------------------------- 1 | {{ define "header" }}{{ end }} 2 | {{ define "main" }} 3 |
4 |
5 |

{{ i18n "pageNotFound" }}

6 |
7 | 8 |
9 |
10 | {{ end }} 11 | 12 | -------------------------------------------------------------------------------- /themes/beautifulhugo/layouts/_default/baseof.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | {{ partial "head.html" . }} 4 | 5 | {{ partial "nav.html" . }} 6 | {{ block "header" . }}{{ partial "header.html" . }}{{ end }} 7 | {{ block "main" . }}{{ end }} 8 | {{ partial "footer.html" . }} 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /themes/beautifulhugo/layouts/_default/list.html: -------------------------------------------------------------------------------- 1 | {{ define "header" }} 2 |
3 |
4 |
5 |
6 |
7 |
8 |

#{{ .Title }}

9 |
10 |
11 |
12 |
13 |
14 |
15 | {{ end }} 16 | {{ define "main" }} 17 |
18 |
19 |
20 |
21 | {{ $pag := .Paginate (where .Data.Pages "Type" "post") }} 22 | {{ range $pag.Pages }} 23 | 55 | {{ end }} 56 |
57 |
58 |
59 |
60 | {{ end }} 61 | 62 | -------------------------------------------------------------------------------- /themes/beautifulhugo/layouts/_default/single.html: -------------------------------------------------------------------------------- 1 | {{ define "main" }} 2 |
3 | {{.Content}} 4 |
5 | {{ end }} 6 | 7 | -------------------------------------------------------------------------------- /themes/beautifulhugo/layouts/_default/terms.html: -------------------------------------------------------------------------------- 1 | {{ define "main" }} 2 | {{ $data := .Data }} 3 |
4 | 12 |
13 | {{ end }} 14 | 15 | -------------------------------------------------------------------------------- /themes/beautifulhugo/layouts/index.html: -------------------------------------------------------------------------------- 1 | {{ define "main" }} 2 |
3 |
4 |
5 | {{ with .Content }} 6 |
7 | {{.}} 8 |
9 | {{ end }} 10 | 11 |
12 | {{ $pag := .Paginate (where .Data.Pages "Type" "post") }} 13 | {{ range $pag.Pages }} 14 | 42 | {{ end }} 43 |
44 | 45 | {{ if or (.Paginator.HasPrev) (.Paginator.HasNext) }} 46 | 58 | {{ end }} 59 |
60 |
61 |
62 | {{ end }} 63 | 64 | -------------------------------------------------------------------------------- /themes/beautifulhugo/layouts/page/single.html: -------------------------------------------------------------------------------- 1 | {{ define "main" }} 2 |
3 |
4 |
5 | {{ .Content }} 6 | {{ if (.Params.comments) | or (and (or (not (isset .Params "comments")) (eq .Params.comments nil)) (.Site.Params.comments)) }} 7 | {{ if .Site.DisqusShortname }} 8 |
9 | {{ template "_internal/disqus.html" . }} 10 |
11 | {{ end }} 12 | {{ end }} 13 |
14 |
15 |
16 | {{ end }} 17 | 18 | -------------------------------------------------------------------------------- /themes/beautifulhugo/layouts/partials/disqus.html: -------------------------------------------------------------------------------- 1 | {{ if (.Params.comments) | or (and (or (not (isset .Params "comments")) (eq .Params.comments nil)) (.Site.Params.comments)) }} 2 | {{ if .Site.DisqusShortname }} 3 |
4 | {{ template "_internal/disqus.html" . }} 5 |
6 | {{ end }} 7 | {{ end }} -------------------------------------------------------------------------------- /themes/beautifulhugo/layouts/partials/footer.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 | 29 | 39 | 40 |

41 | {{ i18n "poweredBy" . | safeHTML }} 42 | {{ with .Site.Params.commit }} • [{{ getenv "GIT_COMMIT_SHA_SHORT" }}]{{ end }} 43 |

44 |
45 |
46 |
47 |
48 | 49 | 50 | 51 | 52 | 53 | 54 | {{- partial "script.html" . -}} 55 | 56 | 57 | 58 | 59 | {{ if ($.Scratch.Get "photoswipeloaded") }} 60 | 61 | 62 | {{ end }} 63 | 64 | {{ if .Site.Params.gcse }} 65 | 76 | {{ end }} 77 | 78 | {{ template "_internal/google_analytics.html" . }} 79 | {{- partial "footer_custom.html" . }} 80 | -------------------------------------------------------------------------------- /themes/beautifulhugo/layouts/partials/footer_custom.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /themes/beautifulhugo/layouts/partials/head.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | {{- with .Title | default .Site.Title }} 7 | {{ . }} 8 | 9 | 10 | {{- end }} 11 | {{- with .Description | default .Params.subtitle | default .Summary }} 12 | 13 | 14 | 15 | {{- end }} 16 | 17 | {{- with .Site.Params.favicon }} 18 | 19 | {{- end -}} 20 | 21 | {{- with .Params.share_img | default .Params.image | default .Site.Params.logo }} 22 | 23 | 24 | {{- end }} 25 | 26 | {{- with .Site.Author.twitter }} 27 | 28 | 29 | {{- end }} 30 | {{- with .Site.Params.fb_app_id }} 31 | 32 | {{- end }} 33 | 34 | 35 | 36 | 37 | {{ .Hugo.Generator -}} 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | {{- partial "head_custom.html" . }} 50 | 51 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /themes/beautifulhugo/layouts/partials/head_custom.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /themes/beautifulhugo/layouts/partials/header.html: -------------------------------------------------------------------------------- 1 | {{ if .IsHome }} 2 | {{ $.Scratch.Set "title" .Site.Title }} 3 | {{ if .Site.Params.subtitle }}{{ $.Scratch.Set "subtitle" .Site.Params.subtitle }}{{ end }} 4 | {{ if .Site.Params.bigimg }}{{ $.Scratch.Set "bigimg" .Site.Params.bigimg }}{{ end }} 5 | {{ else }} 6 | {{ $.Scratch.Set "title" .Title }} 7 | {{ if .Params.subtitle }}{{ $.Scratch.Set "subtitle" .Params.subtitle }}{{ end }} 8 | {{ if .Params.bigimg }}{{ $.Scratch.Set "bigimg" .Params.bigimg }}{{ end }} 9 | {{ end }} 10 | {{ $bigimg := $.Scratch.Get "bigimg" }} 11 | 12 | {{ if or $bigimg ($.Scratch.Get "title") }} 13 | {{ if isset $bigimg 0 }} 14 |
15 | {{ end }} 16 | 17 |
18 | {{ if isset $bigimg 0 }} 19 |
20 | {{ $subtitle := $.Scratch.Get "subtitle" }} 21 |
22 |
23 |
24 |
25 |

{{ with $.Scratch.Get "title" }}{{.}}{{ else }}
{{ end }}

26 | {{ if $subtitle }} 27 | {{ if eq .Type "page" }} 28 |
29 | {{ $subtitle }} 30 | {{ else }} 31 |

{{ $subtitle }}

32 | {{ end }} 33 | {{ end }} 34 | {{ if eq .Type "post" }} 35 | {{ partial "post_meta.html" . }} 36 | {{ end }} 37 |
38 |
39 |
40 |
41 | 42 |
43 | {{end}} 44 |
45 | {{ $subtitle := $.Scratch.Get "subtitle" }} 46 |
47 |
48 |
49 |
50 |

{{ with $.Scratch.Get "title" }}{{.}}{{ else }}
{{ end }}

51 | {{ if $subtitle }} 52 | {{ if eq .Type "page" }} 53 |
54 | {{ $subtitle }} 55 | {{ else }} 56 |

{{ $subtitle }}

57 | {{ end }} 58 | {{ end }} 59 | {{ if eq .Type "post" }} 60 | {{ partial "post_meta.html" . }} 61 | {{ end }} 62 |
63 |
64 |
65 |
66 |
67 |
68 | {{ else }} 69 |
70 | {{ end }} 71 | -------------------------------------------------------------------------------- /themes/beautifulhugo/layouts/partials/nav.html: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 87 | 88 | 89 | {{ if isset .Site.Params "gcse" }} 90 | 106 | {{ end }} 107 | -------------------------------------------------------------------------------- /themes/beautifulhugo/layouts/partials/post_meta.html: -------------------------------------------------------------------------------- 1 | 11 | 12 | -------------------------------------------------------------------------------- /themes/beautifulhugo/layouts/partials/script.html: -------------------------------------------------------------------------------- 1 | 2 | 76 | -------------------------------------------------------------------------------- /themes/beautifulhugo/layouts/partials/translation_link.html: -------------------------------------------------------------------------------- 1 | {{ default .Lang .Site.Language.LanguageName }} 2 | 3 | -------------------------------------------------------------------------------- /themes/beautifulhugo/layouts/post/single.html: -------------------------------------------------------------------------------- 1 | {{ define "main" }} 2 |
3 |
4 |
5 |
6 | {{ .Content }} 7 |
8 | 9 | 21 | 22 | {{ if (.Params.comments) | or (and (or (not (isset .Params "comments")) (eq .Params.comments nil)) (.Site.Params.comments)) }} 23 | {{ if .Site.DisqusShortname }} 24 |
25 | {{ template "_internal/disqus.html" . }} 26 |
27 | {{ end }} 28 | {{ end }} 29 | 30 |
31 |
32 |
33 | {{ end }} 34 | 35 | -------------------------------------------------------------------------------- /themes/beautifulhugo/layouts/shortcodes/figure.html: -------------------------------------------------------------------------------- 1 | 6 | 7 | {{- if not ($.Page.Scratch.Get "figurecount") }}{{ end }} 8 | {{- $.Page.Scratch.Add "figurecount" 1 -}} 9 | 10 | {{- $thumb := .Get "src" | default (printf "%s." (.Get "thumb") | replace (.Get "link") ".") }} 11 |
12 |
13 |
14 | 15 |
16 | {{- if or (or (.Get "title") (.Get "caption")) (.Get "attr")}} 17 |
18 | {{- with .Get "title" }}

{{.}}

{{ end }} 19 | {{- if or (.Get "caption") (.Get "attr")}} 20 |

21 | {{- .Get "caption" -}} 22 | {{- with .Get "attrlink"}}{{ .Get "attr" }}{{ else }}{{ .Get "attr"}}{{ end -}} 23 |

24 | {{- end }} 25 |
26 | {{- end }} 27 | {{ with .Get "link" | default (.Get "src") }}{{ end }} 28 |
29 |
-------------------------------------------------------------------------------- /themes/beautifulhugo/layouts/shortcodes/gallery.html: -------------------------------------------------------------------------------- 1 | 5 | 6 | {{- if not ($.Page.Scratch.Get "figurecount") }}{{ end }} 7 | {{- $.Page.Scratch.Add "figurecount" 1 }} 8 | -------------------------------------------------------------------------------- /themes/beautifulhugo/layouts/shortcodes/load-photoswipe-theme.html: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | {{ if not ($.Page.Scratch.Get "photoswipeloaded") }} 8 | {{ $.Page.Scratch.Set "photoswipeloaded" 1 }} 9 | 10 | 15 | 19 | 20 | 21 | 22 | 23 | 27 | 28 | 29 | 75 | {{ end }} -------------------------------------------------------------------------------- /themes/beautifulhugo/layouts/shortcodes/load-photoswipe.html: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | {{ if not ($.Page.Scratch.Get "photoswipeloaded") }} 8 | {{ $.Page.Scratch.Set "photoswipeloaded" 1 }} 9 | 10 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 71 | {{ end }} -------------------------------------------------------------------------------- /themes/beautifulhugo/static/css/highlight.min.css: -------------------------------------------------------------------------------- 1 | .hljs{display:block;overflow-x:auto;padding:0.5em;background:#f9f9f9;-webkit-text-size-adjust:none}.hljs,.hljs-subst,.hljs-tag .hljs-title,.nginx .hljs-title{color:black}.hljs-string,.hljs-title,.hljs-constant,.hljs-parent,.hljs-tag .hljs-value,.hljs-rule .hljs-value,.hljs-preprocessor,.hljs-pragma,.hljs-name,.haml .hljs-symbol,.ruby .hljs-symbol,.ruby .hljs-symbol .hljs-string,.hljs-template_tag,.django .hljs-variable,.smalltalk .hljs-class,.hljs-addition,.hljs-flow,.hljs-stream,.bash .hljs-variable,.pf .hljs-variable,.apache .hljs-tag,.apache .hljs-cbracket,.tex .hljs-command,.tex .hljs-special,.erlang_repl .hljs-function_or_atom,.asciidoc .hljs-header,.markdown .hljs-header,.coffeescript .hljs-attribute{color:#800}.smartquote,.hljs-comment,.hljs-annotation,.diff .hljs-header,.hljs-chunk,.asciidoc .hljs-blockquote,.markdown .hljs-blockquote{color:#888}.hljs-number,.hljs-date,.hljs-regexp,.hljs-literal,.hljs-hexcolor,.smalltalk .hljs-symbol,.smalltalk .hljs-char,.go .hljs-constant,.hljs-change,.lasso .hljs-variable,.makefile .hljs-variable,.asciidoc .hljs-bullet,.markdown .hljs-bullet,.asciidoc .hljs-link_url,.markdown .hljs-link_url{color:#080}.hljs-label,.hljs-javadoc,.ruby .hljs-string,.hljs-decorator,.hljs-filter .hljs-argument,.hljs-localvars,.hljs-array,.hljs-attr_selector,.hljs-important,.hljs-pseudo,.hljs-pi,.haml .hljs-bullet,.hljs-doctype,.hljs-deletion,.hljs-envvar,.hljs-shebang,.apache .hljs-sqbracket,.nginx .hljs-built_in,.tex .hljs-formula,.erlang_repl .hljs-reserved,.hljs-prompt,.asciidoc .hljs-link_label,.markdown .hljs-link_label,.vhdl .hljs-attribute,.clojure .hljs-attribute,.asciidoc .hljs-attribute,.lasso .hljs-attribute,.coffeescript .hljs-property,.hljs-phony{color:#88f}.hljs-keyword,.hljs-id,.hljs-title,.hljs-built_in,.css .hljs-tag,.hljs-javadoctag,.hljs-phpdoc,.hljs-dartdoc,.hljs-yardoctag,.smalltalk .hljs-class,.hljs-winutils,.bash .hljs-variable,.pf .hljs-variable,.apache .hljs-tag,.hljs-type,.hljs-typename,.tex .hljs-command,.asciidoc .hljs-strong,.markdown .hljs-strong,.hljs-request,.hljs-status{font-weight:bold}.asciidoc .hljs-emphasis,.markdown .hljs-emphasis{font-style:italic}.nginx .hljs-built_in{font-weight:normal}.coffeescript .javascript,.javascript .xml,.lasso .markup,.tex .hljs-formula,.xml .javascript,.xml .vbscript,.xml .css,.xml .hljs-cdata{opacity:0.5} 2 | -------------------------------------------------------------------------------- /themes/beautifulhugo/static/css/hugo-easy-gallery.css: -------------------------------------------------------------------------------- 1 | /* 2 | Put this file in /static/css/hugo-easy-gallery.css 3 | Documentation and licence at https://github.com/liwenyip/hugo-easy-gallery/ 4 | */ 5 | 6 | 7 | /* 8 | Grid Layout Styles 9 | */ 10 | .gallery { 11 | overflow: hidden; 12 | margin: 10px; 13 | max-width: 768px; 14 | } 15 | .gallery .box { 16 | float: left; 17 | position: relative; 18 | /* Default: 1 tile wide */ 19 | width: 100%; 20 | padding-bottom: 100%; 21 | } 22 | @media only screen and (min-width : 365px) { 23 | /* Tablet view: 2 tiles */ 24 | .gallery .box { 25 | width: 50%; 26 | padding-bottom: 50%; 27 | } 28 | } 29 | @media only screen and (min-width : 480px) { 30 | /* Small desktop / ipad view: 3 tiles */ 31 | .gallery .box { 32 | width: 33.3%; 33 | padding-bottom: 33.3%; /* */ 34 | } 35 | } 36 | @media only screen and (min-width : 9999px) { 37 | /* Medium desktop: 4 tiles */ 38 | .box { 39 | width: 25%; 40 | padding-bottom: 25%; 41 | } 42 | } 43 | 44 | /* 45 | Transition styles 46 | */ 47 | .gallery.hover-transition figure, 48 | .gallery.hover-effect-zoom .img, 49 | .gallery:not(.caption-effect-appear) figcaption, 50 | .fancy-figure:not(.caption-effect-appear) figcaption { 51 | -webkit-transition: all 0.3s ease-in-out; 52 | -moz-transition: all 0.3s ease-in-out; 53 | -o-transition: all 0.3s ease-in-out; 54 | transition: all 0.3s ease-in-out; 55 | } 56 | /* 57 | figure styles 58 | */ 59 | figure { 60 | position:relative; /* purely to allow absolution positioning of figcaption */ 61 | overflow: hidden; 62 | } 63 | .gallery figure { 64 | position: absolute; 65 | left: 5px; 66 | right: 5px; 67 | top: 5px; 68 | bottom: 5px; 69 | } 70 | .gallery.hover-effect-grow figure:hover { 71 | transform: scale(1.05); 72 | } 73 | .gallery.hover-effect-shrink figure:hover { 74 | transform: scale(0.95); 75 | } 76 | .gallery.hover-effect-slidedown figure:hover { 77 | transform: translateY(5px); 78 | } 79 | .gallery.hover-effect-slideup figure:hover { 80 | transform: translateY(-5px); 81 | } 82 | 83 | /* 84 | img / a styles 85 | */ 86 | 87 | .gallery .img { 88 | position: absolute; 89 | left: 0; 90 | right: 0; 91 | top: 0; 92 | bottom: 0; 93 | background-size: cover; 94 | background-position: 50% 50%; 95 | background-repeat: no-repeat; 96 | } 97 | .gallery.hover-effect-zoom figure:hover .img { 98 | transform: scale(1.05); 99 | } 100 | .gallery img { 101 | display: none; /* only show the img if not inside a gallery */ 102 | } 103 | figure a { 104 | position: absolute; 105 | left: 0; 106 | right: 0; 107 | top: 0; 108 | bottom: 0; 109 | } 110 | 111 | /* 112 | figcaption styles 113 | */ 114 | .gallery figcaption, 115 | .fancy-figure figcaption { 116 | position: absolute; 117 | bottom: 0; 118 | left: 0; 119 | right: 0; 120 | background: #000; 121 | color: #FFF; 122 | text-align: center; 123 | font-size: 75%; /* change this if you want bigger text */ 124 | background: rgba(0, 0, 0, 0.5); 125 | opacity: 1; 126 | } 127 | .gallery.caption-position-none figcaption, 128 | .fancy-figure.caption-position-none figcaption { 129 | display: none; 130 | } 131 | .gallery.caption-position-center figcaption, 132 | .fancy-figure.caption-position-center figcaption { 133 | top: 0; 134 | padding: 40% 5px; 135 | } 136 | .gallery.caption-position-bottom figcaption, 137 | .fancy-figure.caption-position-bottom figcaption { 138 | padding: 5px; 139 | } 140 | .gallery.caption-effect-fade figure:not(:hover) figcaption, 141 | .gallery.caption-effect-appear figure:not(:hover) figcaption, 142 | .fancy-figure.caption-effect-fade figure:not(:hover) figcaption, 143 | .fancy-figure.caption-effect-appear figure:not(:hover) figcaption { 144 | background: rgba(0, 0, 0, 0); 145 | opacity: 0; 146 | } 147 | .gallery.caption-effect-slide.caption-position-bottom figure:not(:hover) figcaption, 148 | .fancy-figure.caption-effect-slide.caption-position-bottom figure:not(:hover) figcaption { 149 | margin-bottom: -100%; 150 | } 151 | .gallery.caption-effect-slide.caption-position-center figure:not(:hover) figcaption, 152 | .fancy-figure.caption-effect-slide.caption-position-center figure:not(:hover) figcaption { 153 | top: 100%; 154 | } 155 | figcaption p { 156 | margin: auto; /* override style in theme */ 157 | } 158 | -------------------------------------------------------------------------------- /themes/beautifulhugo/static/css/main-minimal.css: -------------------------------------------------------------------------------- 1 | .main-content { 2 | padding-bottom: 50px; 3 | } 4 | 5 | footer.footer-min { 6 | position: fixed; 7 | bottom: 0; 8 | width: 100%; 9 | padding: 3px; 10 | background-color: #f5f5f5; 11 | border-top: 1px solid #eeeeee; 12 | text-align: center; 13 | } -------------------------------------------------------------------------------- /themes/beautifulhugo/static/css/main.css: -------------------------------------------------------------------------------- 1 | @import url("pygment_highlights.css"); 2 | 3 | .highlight pre { 4 | margin-bottom: 0px; margin-top:10px; 5 | } 6 | button.copybtn:hover { 7 | border:solid 2px #777; 8 | padding:2px 3px; 9 | background-color:rgb(128,128,128,0.4); 10 | } 11 | button.copybtn { 12 | border:solid 1px #999; 13 | margin:3px 4px; 14 | padding:3px 4px; 15 | width:37px; 16 | right:14px; 17 | position:absolute; 18 | border-radius:5px; 19 | weight:900; 20 | background: #eee; 21 | } 22 | @media only screen and (min-width: 768px) { 23 | button.copybtn { 24 | left: 98%; 25 | } 26 | } 27 | 28 | /* --- General --- */ 29 | 30 | body { 31 | font-family: 'Lora', 'Times New Roman', serif; 32 | font-size: 18px; 33 | color: #404040; 34 | position: relative; 35 | background: #FFF; 36 | } 37 | p { 38 | line-height: 1.5; 39 | margin: 30px 0; 40 | } 41 | p a { 42 | /* text-decoration: underline */ 43 | color: #008AFF; 44 | } 45 | h1,h2,h3,h4,h5,h6 { 46 | font-family: 'Open Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif; 47 | font-weight: 800; 48 | } 49 | a { 50 | color: #008AFF; 51 | } 52 | a:hover, 53 | a:focus { 54 | color: #0085a1; 55 | } 56 | blockquote { 57 | color: #808080; 58 | font-style: italic; 59 | } 60 | blockquote p:first-child { 61 | margin-top: 0; 62 | } 63 | hr.small { 64 | max-width: 100px; 65 | margin: 15px auto; 66 | border-width: 4px; 67 | border-color: inherit; 68 | border-radius: 3px; 69 | } 70 | 71 | .main-content { 72 | padding-top: 80px; 73 | } 74 | @media only screen and (min-width: 768px) { 75 | .main-content { 76 | padding-top: 130px; 77 | } 78 | } 79 | 80 | .main-explain-area { 81 | font-family: 'Open Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif; 82 | padding: 15px inherit; 83 | } 84 | 85 | .hideme { 86 | display: none; 87 | } 88 | 89 | ::-moz-selection { 90 | color: white; 91 | text-shadow: none; 92 | background: #0085a1; 93 | } 94 | ::selection { 95 | color: white; 96 | text-shadow: none; 97 | background: #0085a1; 98 | } 99 | img::selection { 100 | color: white; 101 | background: transparent; 102 | } 103 | img::-moz-selection { 104 | color: white; 105 | background: transparent; 106 | } 107 | 108 | img { 109 | max-width: 100%; 110 | } 111 | 112 | .disqus-comments { 113 | margin-top: 30px; 114 | } 115 | 116 | @media only screen and (min-width: 768px) { 117 | .disqus-comments { 118 | margin-top: 40px; 119 | } 120 | } 121 | 122 | /* --- Navbar --- */ 123 | 124 | .navbar-custom { 125 | background: #F5F5F5; 126 | border-bottom: 1px solid #EAEAEA; 127 | font-family: 'Open Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif; 128 | } 129 | 130 | .navbar-custom .nav li a { 131 | text-transform: uppercase; 132 | font-size: 12px; 133 | letter-spacing: 1px; 134 | } 135 | 136 | .navbar-custom .navbar-brand, 137 | .navbar-custom .nav li a { 138 | font-weight: 800; 139 | color: #404040; 140 | } 141 | 142 | .navbar-custom .navbar-brand:hover, 143 | .navbar-custom .navbar-brand:focus , 144 | .navbar-custom .nav li a:hover, 145 | .navbar-custom .nav li a:focus { 146 | color: #0085a1; 147 | } 148 | 149 | .navbar-custom .navbar-brand-logo { 150 | padding-top: 0; 151 | -webkit-transition: padding .5s ease-in-out; 152 | -moz-transition: padding .5s ease-in-out; 153 | transition: padding .5s ease-in-out; 154 | } 155 | .navbar-custom .navbar-brand-logo img { 156 | height: 50px; 157 | -webkit-transition: height .5s ease-in-out; 158 | -moz-transition: height .5s ease-in-out; 159 | transition: height .5s ease-in-out; 160 | } 161 | .navbar-custom.top-nav-short .navbar-brand-logo { 162 | padding-top: 5px; 163 | } 164 | .navbar-custom.top-nav-short .navbar-brand-logo img { 165 | height: 40px; 166 | } 167 | 168 | @media only screen and (min-width: 768px) { 169 | .navbar-custom { 170 | padding: 20px 0; 171 | -webkit-transition: background .5s ease-in-out,padding .5s ease-in-out; 172 | -moz-transition: background .5s ease-in-out,padding .5s ease-in-out; 173 | transition: background .5s ease-in-out,padding .5s ease-in-out; 174 | } 175 | 176 | .navbar-custom.top-nav-short { 177 | padding: 0; 178 | } 179 | } 180 | 181 | .navbar-custom .avatar-container { 182 | opacity: 1; 183 | position: absolute; 184 | -webkit-transition: opacity 0.5s ease-in-out; 185 | -moz-transition: opacity 0.5s ease-in-out; 186 | transition: opacity 0.5s ease-in-out; 187 | left: 50%; 188 | width: 50px; 189 | margin-top: -25px; 190 | } 191 | .navbar-custom .avatar-container .avatar-img-border { 192 | width: 100%; 193 | border-radius: 50%; 194 | margin-left: -50%; 195 | display: inline-block; 196 | box-shadow: 0 0 8px rgba(0, 0, 0, .8); 197 | -webkit-box-shadow: 0 0 5px rgba(0, 0, 0, .8); 198 | -moz-box-shadow: 0 0 8px rgba(0, 0, 0, .8); 199 | } 200 | .navbar-custom .avatar-container .avatar-img { 201 | width: 100%; 202 | border-radius: 50%; 203 | display: block; 204 | } 205 | 206 | .navbar-custom.top-nav-short .avatar-container{ 207 | opacity: 0; 208 | } 209 | 210 | .navbar-custom.top-nav-expanded .avatar-container { 211 | display: none; 212 | } 213 | 214 | @media only screen and (min-width: 768px) { 215 | .navbar-custom .avatar-container { 216 | width: 100px; 217 | margin-top: -50px; 218 | } 219 | 220 | .navbar-custom .avatar-container .avatar-img-border { 221 | width: 100%; 222 | box-shadow: 1px 1px 2px rgba(0, 0, 0, .8); 223 | -webkit-box-shadow: 1px 1px 2px rgba(0, 0, 0, .8); 224 | -moz-box-shadow: 1px 1px 2px rgba(0, 0, 0, .8); 225 | } 226 | 227 | .navbar-custom .avatar-container .avatar-img { 228 | width: 100%; 229 | } 230 | } 231 | 232 | /* Multi-level navigation links */ 233 | .navbar-custom .nav .navlinks-container { 234 | position: relative; 235 | } 236 | .navbar-custom .nav .navlinks-parent:after { 237 | content: " \25BC"; 238 | } 239 | .navbar-custom .nav .navlinks-children { 240 | width: 100%; 241 | display: none; 242 | word-break: break-word; 243 | } 244 | .navbar-custom .nav .navlinks-container .navlinks-children a { 245 | display: block; 246 | padding: 10px; 247 | padding-left: 30px; 248 | background: #f5f5f5; 249 | text-decoration: none !important; 250 | border-width: 0 1px 1px 1px; 251 | font-weight: normal; 252 | } 253 | @media only screen and (max-width: 767px) { 254 | .navbar-custom .nav .navlinks-container.show-children { 255 | background: #eee; 256 | } 257 | .navbar-custom .nav .navlinks-container.show-children .navlinks-children { 258 | display: block; 259 | } 260 | } 261 | @media only screen and (min-width: 768px) { 262 | .navbar-custom .nav .navlinks-container { 263 | text-align: center; 264 | } 265 | .navbar-custom .nav .navlinks-container:hover { 266 | background: #eee; 267 | } 268 | .navbar-custom .nav .navlinks-container:hover .navlinks-children { 269 | display: block; 270 | } 271 | .navbar-custom .nav .navlinks-children { 272 | position: absolute; 273 | } 274 | .navbar-custom .nav .navlinks-container .navlinks-children a { 275 | padding-left: 10px; 276 | border: 1px solid #eaeaea; 277 | border-width: 0 1px 1px; 278 | } 279 | } 280 | 281 | /* --- Footer --- */ 282 | 283 | footer { 284 | padding: 30px 0; 285 | background: #F5F5F5; 286 | border-top: 1px #EAEAEA solid; 287 | margin-top: 50px; 288 | font-size: 14px; 289 | } 290 | 291 | footer a { 292 | color: #404040; 293 | } 294 | 295 | footer .list-inline { 296 | margin: 0; 297 | padding: 0; 298 | } 299 | footer .copyright { 300 | font-family: Open Sans; 301 | text-align: center; 302 | margin-bottom: 0; 303 | } 304 | footer .theme-by { 305 | text-align: center; 306 | margin: 10px 0 0; 307 | } 308 | 309 | @media only screen and (min-width: 768px) { 310 | footer { 311 | padding: 50px 0; 312 | } 313 | footer .footer-links { 314 | font-size: 18px; 315 | } 316 | footer .copyright { 317 | font-size: 16px; 318 | } 319 | } 320 | 321 | /* --- Post preview --- */ 322 | 323 | .post-preview { 324 | padding: 20px 0; 325 | border-bottom: 1px solid #eee; 326 | } 327 | 328 | @media only screen and (min-width: 768px) { 329 | .post-preview { 330 | padding: 35px 0; 331 | } 332 | } 333 | 334 | .post-preview:last-child { 335 | border-bottom: 0; 336 | } 337 | 338 | .post-preview a { 339 | text-decoration: none; 340 | color: #404040; 341 | } 342 | 343 | .post-preview a:focus, 344 | .post-preview a:hover { 345 | text-decoration: none; 346 | color: #0085a1; 347 | } 348 | 349 | .post-preview .post-title { 350 | font-size: 30px; 351 | margin-top: 0; 352 | } 353 | .post-preview .post-subtitle { 354 | margin: 0; 355 | font-weight: 300; 356 | margin-bottom: 10px; 357 | } 358 | .post-preview .post-meta, 359 | .post-heading .post-meta { 360 | color: #808080; 361 | font-size: 18px; 362 | font-style: italic; 363 | margin: 0 0 10px; 364 | } 365 | .post-preview .post-meta a, 366 | .post-heading .post-meta a { 367 | color: #404040; 368 | text-decoration: none; 369 | } 370 | .post-preview .post-entry { 371 | font-family: 'Open Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif; 372 | } 373 | .post-entry-container { 374 | display: inline-block; 375 | width: 100%; 376 | } 377 | .post-entry { 378 | width: 100%; 379 | } 380 | .post-image { 381 | float: right; 382 | height: 192px; 383 | width: 192px; 384 | margin-top: -35px; 385 | filter: grayscale(90%); 386 | } 387 | .post-image:hover { 388 | filter: grayscale(0%); 389 | } 390 | .post-image img { 391 | border-radius: 100px; 392 | height: 192px; 393 | width: 192px; 394 | } 395 | .post-preview .post-read-more { 396 | font-weight: 800; 397 | float: right; 398 | } 399 | 400 | @media only screen and (min-width: 768px) { 401 | .post-preview .post-title { 402 | font-size: 36px; 403 | } 404 | } 405 | 406 | /* --- Tags --- */ 407 | 408 | .blog-tags { 409 | font-family: 'Open Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif; 410 | color: #999; 411 | font-size: 15px; 412 | margin-bottom: 30px; 413 | } 414 | 415 | .blog-tags a { 416 | color: #008AFF; 417 | text-decoration: none; 418 | padding: 0px 5px; 419 | } 420 | 421 | .blog-tags a:hover { 422 | border-radius: 2px; 423 | color: #008AFF; 424 | background-color: #CCC; 425 | } 426 | 427 | .post-preview .blog-tags { 428 | margin-top: 5px; 429 | margin-bottom: 0; 430 | } 431 | 432 | @media only screen and (min-width: 768px) { 433 | .post-preview .blog-tags { 434 | margin-top: 10px; 435 | } 436 | } 437 | 438 | @media only screen and (max-width: 500px) { 439 | .post-image, .post-image img { 440 | height: 100px; 441 | width: 100px; 442 | } 443 | 444 | .post-image { 445 | width: 100%; 446 | text-align: center; 447 | margin-top: 0; 448 | float: left; 449 | } 450 | } 451 | /* --- Post and page headers --- */ 452 | 453 | .intro-header { 454 | margin: 80px 0 20px; 455 | position: relative; 456 | } 457 | .intro-header.big-img { 458 | background: no-repeat center center; 459 | -webkit-background-size: cover; 460 | -moz-background-size: cover; 461 | background-size: cover; 462 | -o-background-size: cover; 463 | margin-top: 51px; /* The small navbar is 50px tall + 1px border */ 464 | margin-bottom: 35px; 465 | } 466 | .intro-header.big-img .big-img-transition { 467 | position: absolute; 468 | width: 100%; 469 | height: 100%; 470 | opacity: 0; 471 | background: no-repeat center center; 472 | -webkit-background-size: cover; 473 | -moz-background-size: cover; 474 | background-size: cover; 475 | -o-background-size: cover; 476 | -webkit-transition: opacity 1s linear; 477 | -moz-transition: opacity 1s linear; 478 | transition: opacity 1s linear; 479 | } 480 | .intro-header .page-heading { 481 | text-align: center; 482 | } 483 | .intro-header.big-img .page-heading, 484 | .intro-header.big-img .post-heading { 485 | padding: 100px 0; 486 | color: #FFF; 487 | text-shadow: 1px 1px 3px #000; 488 | } 489 | .intro-header .page-heading h1 { 490 | margin-top: 0; 491 | font-size: 30px; 492 | } 493 | .intro-header .post-heading h1 { 494 | margin-top: 0; 495 | font-size: 35px; 496 | } 497 | .intro-header .page-heading .page-subheading, 498 | .intro-header .post-heading .post-subheading { 499 | font-size: 27px; 500 | line-height: 1.1; 501 | display: block; 502 | font-family: 'Open Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif; 503 | font-weight: 300; 504 | margin: 10px 0 0; 505 | } 506 | .intro-header .post-heading .post-subheading { 507 | margin-bottom: 20px; 508 | } 509 | .intro-header.big-img .page-heading .page-subheading, 510 | .intro-header.big-img .post-heading .post-subheading { 511 | font-weight: 400; 512 | } 513 | .intro-header.big-img .page-heading hr { 514 | box-shadow: 1px 1px 3px #000; 515 | -webkit-box-shadow: 1px 1px 3px #000; 516 | -moz-box-shadow: 1px 1px 3px #000; 517 | } 518 | .intro-header.big-img .post-heading .post-meta { 519 | color: #EEE; 520 | } 521 | .intro-header.big-img .img-desc { 522 | background: rgba(30, 30, 30, 0.6); 523 | position: absolute; 524 | padding: 5px 10px; 525 | font-size: 11px; 526 | color: #EEE; 527 | font-family: 'Open Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif; 528 | right: 0; 529 | bottom: 0; 530 | display: none; 531 | } 532 | @media only screen and (min-width: 768px) { 533 | .intro-header { 534 | margin-top: 130px; 535 | } 536 | .intro-header.big-img { 537 | margin-top: 91px; /* Full navbar is small navbar + 20px padding on each side when expanded */ 538 | } 539 | .intro-header.big-img .page-heading, 540 | .intro-header.big-img .post-heading { 541 | padding: 150px 0; 542 | } 543 | .intro-header .page-heading h1 { 544 | font-size: 50px; 545 | } 546 | .intro-header .post-heading h1 { 547 | font-size: 30px; 548 | } 549 | .intro-header.big-img .img-desc { 550 | font-size: 14px; 551 | } 552 | } 553 | 554 | .header-section.has-img .no-img { 555 | margin-top: 0; 556 | background: #FCFCFC; 557 | margin: 0 0 40px; 558 | padding: 20px 0; 559 | box-shadow: 0 0 5px #AAA; 560 | } 561 | /* Many phones are 320 or 360px, so make sure images are a proper aspect ratio in those cases */ 562 | .header-section.has-img .intro-header.no-img { 563 | display: none; 564 | } 565 | @media only screen and (max-width: 365px) { 566 | .header-section.has-img .intro-header.no-img { 567 | display: block; 568 | } 569 | .intro-header.big-img { 570 | width: 100%; 571 | height: 220px; 572 | } 573 | .intro-header.big-img .page-heading, 574 | .intro-header.big-img .post-heading { 575 | display: none; 576 | } 577 | .header-section.has-img .big-img { 578 | margin-bottom: 0; 579 | } 580 | } 581 | @media only screen and (max-width: 325px) { 582 | .intro-header.big-img { 583 | height: 200px; 584 | } 585 | } 586 | 587 | .caption { 588 | text-align: center; 589 | font-size: 14px; 590 | padding: 10px; 591 | font-style: italic; 592 | margin: 0; 593 | display: block; 594 | border-bottom-right-radius: 5px; 595 | border-bottom-left-radius: 5px; 596 | } 597 | 598 | /* --- Pager --- */ 599 | 600 | .pager li a { 601 | font-family: 'Open Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif; 602 | text-transform: uppercase; 603 | font-size: 14px; 604 | font-weight: 800; 605 | letter-spacing: 1px; 606 | padding: 10px 5px; 607 | background: #FFF; 608 | border-radius: 0; 609 | color: #404040; 610 | } 611 | @media only screen and (min-width: 768px) { 612 | .pager li a { 613 | padding: 15px 25px; 614 | } 615 | } 616 | .pager li a:hover, 617 | .pager li a:focus { 618 | color: #FFF; 619 | background: #0085a1; 620 | border: 1px solid #0085a1; 621 | } 622 | 623 | .pager { 624 | margin: 10px 0 0; 625 | } 626 | 627 | .pager.blog-pager { 628 | margin-top: 0; 629 | } 630 | 631 | @media only screen and (min-width: 768px) { 632 | .pager.blog-pager { 633 | margin-top: 10px; 634 | } 635 | } 636 | 637 | /* --- Tables --- */ 638 | 639 | table { 640 | padding: 0; 641 | } 642 | table tr { 643 | border-top: 1px solid #cccccc; 644 | background-color: #ffffff; 645 | margin: 0; 646 | padding: 0; 647 | } 648 | table tr:nth-child(2n) { 649 | background-color: #f8f8f8; 650 | } 651 | table tr th { 652 | font-weight: bold; 653 | border: 1px solid #cccccc; 654 | text-align: left; 655 | margin: 0; 656 | padding: 6px 13px; 657 | } 658 | table tr td { 659 | border: 1px solid #cccccc; 660 | text-align: left; 661 | margin: 0; 662 | padding: 6px 13px; 663 | } 664 | table tr th :first-child, 665 | table tr td :first-child { 666 | margin-top: 0; 667 | } 668 | table tr th :last-child, 669 | table tr td :last-child { 670 | margin-bottom: 0; 671 | } 672 | 673 | /* --- Code blocks --- */ 674 | 675 | pre { 676 | font-size: 16px; 677 | line-height: 1.5em; 678 | } 679 | pre code { 680 | white-space: pre; 681 | } 682 | pre.highlight, .highlight > pre, td.code pre { 683 | background: #FAFAFA; 684 | background-image: linear-gradient(#F9F9F9 50%, #FDFDFD 50%); 685 | background-repeat: repeat; 686 | background-size: 3em 3em; 687 | background-position: 0px 10px; 688 | border-left: 7px solid #444; 689 | } 690 | code table, code table td, code table th, code table tbody, code table tr, 691 | td.gutter pre { 692 | padding: 0; 693 | border: none; 694 | background-color: #fff; 695 | } 696 | .highlight > pre { 697 | padding: 0; 698 | } 699 | td.code pre { 700 | border-width: 0 0 0 2px; 701 | border-style: solid; 702 | border-color: #444; 703 | border-radius: 0; 704 | } 705 | td.gutter { 706 | padding-top: 3px; 707 | } 708 | 709 | /* --- Social media sharing section --- */ 710 | 711 | #social-share-section { 712 | margin-bottom: 30px; 713 | } 714 | 715 | /* --- Google Custom Search Engine Popup --- */ 716 | #modalSearch table tr, #modalSearch table tr td, #modalSearch table tr th { 717 | border:none; 718 | } 719 | .reset-box-sizing, .reset-box-sizing *, .reset-box-sizing *:before, .reset-box-sizing *:after, .gsc-inline-block { 720 | -webkit-box-sizing: content-box; 721 | -moz-box-sizing: content-box; 722 | box-sizing: content-box; 723 | } 724 | input.gsc-input, .gsc-input-box, .gsc-input-box-hover, .gsc-input-box-focus, .gsc-search-button { 725 | box-sizing: content-box; 726 | line-height: normal; 727 | } 728 | -------------------------------------------------------------------------------- /themes/beautifulhugo/static/css/pygment_highlights.css: -------------------------------------------------------------------------------- 1 | /* .highlight { background: #ffffff; } */ 2 | /* .highlight pre { background-color: #fff; font-size: 16px } */ 3 | .highlight .c { color: #999988; font-style: italic } /* Comment */ 4 | .highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */ 5 | .highlight .k { font-weight: bold } /* Keyword */ 6 | .highlight .o { font-weight: bold } /* Operator */ 7 | .highlight .cm { color: #999988; font-style: italic } /* Comment.Multiline */ 8 | .highlight .cp { color: #999999; font-weight: bold } /* Comment.Preproc */ 9 | .highlight .c1 { color: #999988; font-style: italic } /* Comment.Single */ 10 | .highlight .cs { color: #999999; font-weight: bold; font-style: italic } /* Comment.Special */ 11 | .highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */ 12 | .highlight .gd .x { color: #000000; background-color: #ffaaaa } /* Generic.Deleted.Specific */ 13 | .highlight .ge { font-style: italic } /* Generic.Emph */ 14 | .highlight .gr { color: #aa0000 } /* Generic.Error */ 15 | .highlight .gh { color: #999999 } /* Generic.Heading */ 16 | .highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */ 17 | .highlight .gi .x { color: #000000; background-color: #aaffaa } /* Generic.Inserted.Specific */ 18 | .highlight .go { color: #888888 } /* Generic.Output */ 19 | .highlight .gp { color: #555555 } /* Generic.Prompt */ 20 | .highlight .gs { font-weight: bold } /* Generic.Strong */ 21 | .highlight .gu { color: #aaaaaa } /* Generic.Subheading */ 22 | .highlight .gt { color: #aa0000 } /* Generic.Traceback */ 23 | .highlight .kc { font-weight: bold } /* Keyword.Constant */ 24 | .highlight .kd { font-weight: bold } /* Keyword.Declaration */ 25 | .highlight .kp { font-weight: bold } /* Keyword.Pseudo */ 26 | .highlight .kr { font-weight: bold } /* Keyword.Reserved */ 27 | .highlight .kt { color: #445588; font-weight: bold } /* Keyword.Type */ 28 | .highlight .m { color: #009999 } /* Literal.Number */ 29 | .highlight .s { color: #d14 } /* Literal.String */ 30 | .highlight .na { color: #008080 } /* Name.Attribute */ 31 | .highlight .nb { color: #0086B3 } /* Name.Builtin */ 32 | .highlight .nc { color: #445588; font-weight: bold } /* Name.Class */ 33 | .highlight .no { color: #008080 } /* Name.Constant */ 34 | .highlight .ni { color: #800080 } /* Name.Entity */ 35 | .highlight .ne { color: #990000; font-weight: bold } /* Name.Exception */ 36 | .highlight .nf { color: #990000; font-weight: bold } /* Name.Function */ 37 | .highlight .nn { color: #555555 } /* Name.Namespace */ 38 | .highlight .nt { color: #000080 } /* Name.Tag */ 39 | .highlight .nv { color: #008080 } /* Name.Variable */ 40 | .highlight .ow { font-weight: bold } /* Operator.Word */ 41 | .highlight .w { color: #bbbbbb } /* Text.Whitespace */ 42 | .highlight .mf { color: #009999 } /* Literal.Number.Float */ 43 | .highlight .mh { color: #009999 } /* Literal.Number.Hex */ 44 | .highlight .mi { color: #009999 } /* Literal.Number.Integer */ 45 | .highlight .mo { color: #009999 } /* Literal.Number.Oct */ 46 | .highlight .sb { color: #d14 } /* Literal.String.Backtick */ 47 | .highlight .sc { color: #d14 } /* Literal.String.Char */ 48 | .highlight .sd { color: #d14 } /* Literal.String.Doc */ 49 | .highlight .s2 { color: #d14 } /* Literal.String.Double */ 50 | .highlight .se { color: #d14 } /* Literal.String.Escape */ 51 | .highlight .sh { color: #d14 } /* Literal.String.Heredoc */ 52 | .highlight .si { color: #d14 } /* Literal.String.Interpol */ 53 | .highlight .sx { color: #d14 } /* Literal.String.Other */ 54 | .highlight .sr { color: #009926 } /* Literal.String.Regex */ 55 | .highlight .s1 { color: #d14 } /* Literal.String.Single */ 56 | .highlight .ss { color: #990073 } /* Literal.String.Symbol */ 57 | .highlight .bp { color: #999999 } /* Name.Builtin.Pseudo */ 58 | .highlight .vc { color: #008080 } /* Name.Variable.Class */ 59 | .highlight .vg { color: #008080 } /* Name.Variable.Global */ 60 | .highlight .vi { color: #008080 } /* Name.Variable.Instance */ 61 | .highlight .il { color: #009999 } /* Literal.Number.Integer.Long */ 62 | 63 | /* Make line numbers unselectable: excludes line numbers from copy-paste user ops */ 64 | .highlight .lineno {color:rgba(0,0,0,0.3);padding: 0 10px;-webkit-user-select: none;-moz-user-select: none; -o-user-select: none;} 65 | .lineno::-moz-selection {background-color: transparent;} /* Mozilla specific */ 66 | .lineno::selection {background-color: transparent;} /* Other major browsers */ 67 | -------------------------------------------------------------------------------- /themes/beautifulhugo/static/img/404-southpark.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openshift-evangelists/kbe/445a629a29bdca5280bb94c2f455129606e08c19/themes/beautifulhugo/static/img/404-southpark.jpg -------------------------------------------------------------------------------- /themes/beautifulhugo/static/img/avatar-favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openshift-evangelists/kbe/445a629a29bdca5280bb94c2f455129606e08c19/themes/beautifulhugo/static/img/avatar-favicon.png -------------------------------------------------------------------------------- /themes/beautifulhugo/static/img/avatar-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openshift-evangelists/kbe/445a629a29bdca5280bb94c2f455129606e08c19/themes/beautifulhugo/static/img/avatar-icon.png -------------------------------------------------------------------------------- /themes/beautifulhugo/static/img/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openshift-evangelists/kbe/445a629a29bdca5280bb94c2f455129606e08c19/themes/beautifulhugo/static/img/favicon.ico -------------------------------------------------------------------------------- /themes/beautifulhugo/static/img/favicon.ico.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openshift-evangelists/kbe/445a629a29bdca5280bb94c2f455129606e08c19/themes/beautifulhugo/static/img/favicon.ico.zip -------------------------------------------------------------------------------- /themes/beautifulhugo/static/img/hexagon-thumb.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openshift-evangelists/kbe/445a629a29bdca5280bb94c2f455129606e08c19/themes/beautifulhugo/static/img/hexagon-thumb.jpg -------------------------------------------------------------------------------- /themes/beautifulhugo/static/img/hexagon.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openshift-evangelists/kbe/445a629a29bdca5280bb94c2f455129606e08c19/themes/beautifulhugo/static/img/hexagon.jpg -------------------------------------------------------------------------------- /themes/beautifulhugo/static/img/path.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openshift-evangelists/kbe/445a629a29bdca5280bb94c2f455129606e08c19/themes/beautifulhugo/static/img/path.jpg -------------------------------------------------------------------------------- /themes/beautifulhugo/static/img/sphere-thumb.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openshift-evangelists/kbe/445a629a29bdca5280bb94c2f455129606e08c19/themes/beautifulhugo/static/img/sphere-thumb.jpg -------------------------------------------------------------------------------- /themes/beautifulhugo/static/img/sphere.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openshift-evangelists/kbe/445a629a29bdca5280bb94c2f455129606e08c19/themes/beautifulhugo/static/img/sphere.jpg -------------------------------------------------------------------------------- /themes/beautifulhugo/static/img/triangle-thumb.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openshift-evangelists/kbe/445a629a29bdca5280bb94c2f455129606e08c19/themes/beautifulhugo/static/img/triangle-thumb.jpg -------------------------------------------------------------------------------- /themes/beautifulhugo/static/img/triangle.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openshift-evangelists/kbe/445a629a29bdca5280bb94c2f455129606e08c19/themes/beautifulhugo/static/img/triangle.jpg -------------------------------------------------------------------------------- /themes/beautifulhugo/static/js/load-photoswipe.js: -------------------------------------------------------------------------------- 1 | /* 2 | Put this file in /static/js/load-photoswipe.js 3 | Documentation and licence at https://github.com/liwenyip/hugo-easy-gallery/ 4 | */ 5 | 6 | /* TODO: Make the share function work */ 7 | $( document ).ready(function() { 8 | /* 9 | Initialise Photoswipe 10 | */ 11 | var items = []; // array of slide objects that will be passed to PhotoSwipe() 12 | // for every figure element on the page: 13 | $('figure').each( function() { 14 | if ($(this).attr('class') == 'no-photoswipe') return true; // ignore any figures where class="no-photoswipe" 15 | // get properties from child a/img/figcaption elements, 16 | var $figure = $(this), 17 | $a = $figure.find('a'), 18 | $img = $figure.find('img'), 19 | $src = $a.attr('href'), 20 | $title = $img.attr('alt'), 21 | $msrc = $img.attr('src'); 22 | // if data-size on tag is set, read it and create an item 23 | if ($a.data('size')) { 24 | var $size = $a.data('size').split('x'); 25 | var item = { 26 | src : $src, 27 | w : $size[0], 28 | h : $size[1], 29 | title : $title, 30 | msrc : $msrc 31 | }; 32 | console.log("Using pre-defined dimensions for " + $src); 33 | // if not, set temp default size then load the image to check actual size 34 | } else { 35 | var item = { 36 | src : $src, 37 | w : 800, // temp default size 38 | h : 600, // temp default size 39 | title : $title, 40 | msrc : $msrc 41 | }; 42 | console.log("Using default dimensions for " + $src); 43 | // load the image to check its dimensions 44 | // update the item as soon as w and h are known (check every 30ms) 45 | var img = new Image(); 46 | img.src = $src; 47 | var wait = setInterval(function() { 48 | var w = img.naturalWidth, 49 | h = img.naturalHeight; 50 | if (w && h) { 51 | clearInterval(wait); 52 | item.w = w; 53 | item.h = h; 54 | console.log("Got actual dimensions for " + img.src); 55 | } 56 | }, 30); 57 | } 58 | // Save the index of this image then add it to the array 59 | var index = items.length; 60 | items.push(item); 61 | // Event handler for click on a figure 62 | $figure.on('click', function(event) { 63 | event.preventDefault(); // prevent the normal behaviour i.e. load the hyperlink 64 | // Get the PSWP element and initialise it with the desired options 65 | var $pswp = $('.pswp')[0]; 66 | var options = { 67 | index: index, 68 | bgOpacity: 0.8, 69 | showHideOpacity: true 70 | } 71 | new PhotoSwipe($pswp, PhotoSwipeUI_Default, items, options).init(); 72 | }); 73 | }); 74 | }); -------------------------------------------------------------------------------- /themes/beautifulhugo/static/js/main.js: -------------------------------------------------------------------------------- 1 | // Dean Attali / Beautiful Jekyll 2016 2 | 3 | var main = { 4 | 5 | bigImgEl : null, 6 | numImgs : null, 7 | 8 | init : function() { 9 | // Shorten the navbar after scrolling a little bit down 10 | $(window).scroll(function() { 11 | if ($(".navbar").offset().top > 50) { 12 | $(".navbar").addClass("top-nav-short"); 13 | } else { 14 | $(".navbar").removeClass("top-nav-short"); 15 | } 16 | }); 17 | 18 | // On mobile, hide the avatar when expanding the navbar menu 19 | $('#main-navbar').on('show.bs.collapse', function () { 20 | $(".navbar").addClass("top-nav-expanded"); 21 | }); 22 | $('#main-navbar').on('hidden.bs.collapse', function () { 23 | $(".navbar").removeClass("top-nav-expanded"); 24 | }); 25 | 26 | // On mobile, when clicking on a multi-level navbar menu, show the child links 27 | $('#main-navbar').on("click", ".navlinks-parent", function(e) { 28 | var target = e.target; 29 | $.each($(".navlinks-parent"), function(key, value) { 30 | if (value == target) { 31 | $(value).parent().toggleClass("show-children"); 32 | } else { 33 | $(value).parent().removeClass("show-children"); 34 | } 35 | }); 36 | }); 37 | 38 | // Ensure nested navbar menus are not longer than the menu header 39 | var menus = $(".navlinks-container"); 40 | if (menus.length > 0) { 41 | var navbar = $("#main-navbar ul"); 42 | var fakeMenuHtml = ""; 43 | navbar.append(fakeMenuHtml); 44 | var fakeMenu = $(".fake-menu"); 45 | 46 | $.each(menus, function(i) { 47 | var parent = $(menus[i]).find(".navlinks-parent"); 48 | var children = $(menus[i]).find(".navlinks-children a"); 49 | var words = []; 50 | $.each(children, function(idx, el) { words = words.concat($(el).text().trim().split(/\s+/)); }); 51 | var maxwidth = 0; 52 | $.each(words, function(id, word) { 53 | fakeMenu.html("" + word + ""); 54 | var width = fakeMenu.width(); 55 | if (width > maxwidth) { 56 | maxwidth = width; 57 | } 58 | }); 59 | $(menus[i]).css('min-width', maxwidth + 'px') 60 | }); 61 | 62 | fakeMenu.remove(); 63 | } 64 | 65 | // show the big header image 66 | main.initImgs(); 67 | }, 68 | 69 | initImgs : function() { 70 | // If the page was large images to randomly select from, choose an image 71 | if ($("#header-big-imgs").length > 0) { 72 | main.bigImgEl = $("#header-big-imgs"); 73 | main.numImgs = main.bigImgEl.attr("data-num-img"); 74 | 75 | // 2fc73a3a967e97599c9763d05e564189 76 | // set an initial image 77 | var imgInfo = main.getImgInfo(); 78 | var src = imgInfo.src; 79 | var desc = imgInfo.desc; 80 | main.setImg(src, desc); 81 | 82 | // For better UX, prefetch the next image so that it will already be loaded when we want to show it 83 | var getNextImg = function() { 84 | var imgInfo = main.getImgInfo(); 85 | var src = imgInfo.src; 86 | var desc = imgInfo.desc; 87 | 88 | var prefetchImg = new Image(); 89 | prefetchImg.src = src; 90 | // if I want to do something once the image is ready: `prefetchImg.onload = function(){}` 91 | 92 | setTimeout(function(){ 93 | var img = $("
").addClass("big-img-transition").css("background-image", 'url(' + src + ')'); 94 | $(".intro-header.big-img").prepend(img); 95 | setTimeout(function(){ img.css("opacity", "1"); }, 50); 96 | 97 | // after the animation of fading in the new image is done, prefetch the next one 98 | //img.one("transitioned webkitTransitionEnd oTransitionEnd MSTransitionEnd", function(){ 99 | setTimeout(function() { 100 | main.setImg(src, desc); 101 | img.remove(); 102 | getNextImg(); 103 | }, 1000); 104 | //}); 105 | }, 6000); 106 | }; 107 | 108 | // If there are multiple images, cycle through them 109 | if (main.numImgs > 1) { 110 | getNextImg(); 111 | } 112 | } 113 | }, 114 | 115 | getImgInfo : function() { 116 | var randNum = Math.floor((Math.random() * main.numImgs) + 1); 117 | var src = main.bigImgEl.attr("data-img-src-" + randNum); 118 | var desc = main.bigImgEl.attr("data-img-desc-" + randNum); 119 | 120 | return { 121 | src : src, 122 | desc : desc 123 | } 124 | }, 125 | 126 | setImg : function(src, desc) { 127 | $(".intro-header.big-img").css("background-image", 'url(' + src + ')'); 128 | if (typeof desc !== typeof undefined && desc !== false) { 129 | $(".img-desc").text(desc).show(); 130 | } else { 131 | $(".img-desc").hide(); 132 | } 133 | } 134 | }; 135 | 136 | // 2fc73a3a967e97599c9763d05e564189 137 | 138 | document.addEventListener('DOMContentLoaded', main.init); -------------------------------------------------------------------------------- /themes/beautifulhugo/theme.toml: -------------------------------------------------------------------------------- 1 | name = "Beautiful Hugo" 2 | license = "MIT" 3 | licenselink = "https://github.com/halogenica/Hugo-BeautifulHugo/blob/master/LICENSE" 4 | description = "A port of Beautiful Jekyll theme" 5 | tags = ["blog", "company", "portfolio", "projects", "minimal", "responsive"] 6 | features = ["blog", "themes", "disqus", "minimal", "responsive"] 7 | min_version = 0.17 8 | 9 | [author] 10 | name = "halogenica" 11 | homepage = "http://halogenica.net" 12 | 13 | # If Porting existing theme 14 | [original] 15 | author = "dattali" 16 | homepage = "http://deanattali.com/beautiful-jekyll/" 17 | repo = "https://github.com/daattali/beautiful-jekyll" 18 | 19 | --------------------------------------------------------------------------------